1 2 3 Previous Next 74 Replies Latest reply on Jun 27, 2013 2:13 PM by user5636757

    JNDI, Active Directory (Creating new users & demystifying userAccountContro

    800477
      Creating a new user is a very straightforward task, although with Microsoft improving the security in Windows Server 2003, and adding password complexity requirements, there are a few more hoops to go through.

      If you understand the logic, it's quite simple.

      First of all, every object class has a set of mandatory attributes. If these are not set, then an object cannot be instantiated. These can be determined from the schema definition of the class

      For a user object, the mandatory attributes are commonName (cn), samAccountName and objectClass.

      Then you have the chicken & egg situation.

      The new password complexity requires that an enabled user account must have a password that meets the complexity requirements, but you can't set the password on a user object until it has been created.

      So it's a two step process:

      1. Create a user, with no password, and userAccountControl set to a Normal Account, Account Disabled and Password not Required.
      2. Set the password and change the userAccountControl attribute to Normal Account and whatever other options, such as User Must Change Password at Next Logon.

      (These two steps don't have to be done together, you may precreate the account and then set the password and enable it at some other time)

      To understand the userAccountControl bit masks, these are defined in lmaccess.h which you can find in the Microsoft Windows platform SDK.

      If you create a user, with a blank password but do not set the userAccountControl to disable the account, you will get the usual UNWILLING TO PERFORM error message.

      Here is a simple example of code to create a user account. (Note that it also uses TLS to enable the password to be set).
      /**
       * newuser.java
       * 5 July 2001
       * updated December 2004 for WIn2K3 Password complexity
       * and to use TLS during password set
       * Sample JNDI application that creates a new user in the Active Directory.
       * 
       */
      
      import java.util.Hashtable;
      import javax.naming.ldap.*;
      import javax.naming.directory.*;
      import javax.naming.*;
      import javax.net.ssl.*;
      import java.io.*;
      
      public class newuser
      {
           public static void main (String[] args)
           {
           
                Hashtable env = new Hashtable();
                String adminName = "CN=Administrator,CN=Users,DC=antipodes,DC=com";
                String adminPassword = "XXXXXXX";
                String userName = "CN=Albert Einstein,OU=Research,DC=antipodes,DC=com";
                String groupName = "CN=All Research,OU=Research,DC=antipodes,DC=com";
                
                env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
           
                //set security credentials, note using simple cleartext authentication
                env.put(Context.SECURITY_AUTHENTICATION,"simple");
                env.put(Context.SECURITY_PRINCIPAL,adminName);
                env.put(Context.SECURITY_CREDENTIALS,adminPassword);
                          
                //connect to my domain controller
                env.put(Context.PROVIDER_URL, "ldap://mydc.antipodes.com:389");
                          
                try {
      
                     // Create the initial directory context
                     LdapContext ctx = new InitialLdapContext(env,null);
      
                     // Create attributes to be associated with the new user
                         Attributes attrs = new BasicAttributes(true); 
                    
                     //These are the mandatory attributes for a user object
                     //Note that Win2K3 will automagically create a random 
                     //samAccountName if it is not present. (Win2K does not)
                     attrs.put("objectClass","user");
                         attrs.put("samAccountName","AlbertE");
                     attrs.put("cn","Albert Einstein");
      
                     //These are some optional (but useful) attributes
                     attrs.put("giveName","Albert");
                     attrs.put("sn","Einstein");
                     attrs.put("displayName","Albert Einstein");
                     attrs.put("description","Research Scientist");
                         attrs.put("userPrincipalName","AlbertE@antipodes.com");
                         attrs.put("mail","relativity@antipodes.com");
                     attrs.put("telephoneNumber","999 123 4567");
                     
                     //some useful constants from lmaccess.h
                     int UF_ACCOUNTDISABLE = 0x0002;
                     int UF_PASSWD_NOTREQD = 0x0020;
                     int UF_PASSWD_CANT_CHANGE = 0x0040;
                     int UF_NORMAL_ACCOUNT = 0x0200;
                     int UF_DONT_EXPIRE_PASSWD = 0x10000;
                     int UF_PASSWORD_EXPIRED = 0x800000;
                
                     //Note that you need to create the user object before you can
                     //set the password. Therefore as the user is created with no 
                     //password, user AccountControl must be set to the following
                     //otherwise the Win2K3 password filter will return error 53
                     //unwilling to perform.
      
                         attrs.put("userAccountControl",Integer.toString(UF_NORMAL_ACCOUNT + UF_PASSWD_NOTREQD + UF_PASSWORD_EXPIRED+ UF_ACCOUNTDISABLE));
      
                
                     // Create the context
                     Context result = ctx.createSubcontext(userName, attrs);
                     System.out.println("Created disabled account for: " + userName);
      
                     //now that we've created the user object, we can set the 
                     //password and change the userAccountControl
                     //and because password can only be set using SSL/TLS
                     //lets use StartTLS
      
                     StartTlsResponse tls = (StartTlsResponse)ctx.extendedOperation(new StartTlsRequest());
                     tls.negotiate();
                
                     //set password is a ldap modfy operation
                     //and we'll update the userAccountControl
                     //enabling the acount and force the user to update ther password
                     //the first time they login
                     ModificationItem[] mods = new ModificationItem[2];
                
                     //Replace the "unicdodePwd" attribute with a new value
                     //Password must be both Unicode and a quoted string
                     String newQuotedPassword = "\"Password2000\"";
                     byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
      
                     mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword));
                     mods[1] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("userAccountControl",Integer.toString(UF_NORMAL_ACCOUNT + UF_PASSWORD_EXPIRED)));
                
                     // Perform the update
                     ctx.modifyAttributes(userName, mods);
                     System.out.println("Set password & updated userccountControl");
      
      
                     //now add the user to a group.
      
                          try     {
                               ModificationItem member[] = new ModificationItem[1];
                               member[0]= new ModificationItem(DirContext.ADD_ATTRIBUTE, new BasicAttribute("member", userName)); 
                          
                               ctx.modifyAttributes(groupName,member);
                               System.out.println("Added user to group: " + groupName);
      
                          } 
                          catch (NamingException e) {
                                System.err.println("Problem adding user to group: " + e);
                          }
                     //Could have put tls.close()  prior to the group modification
                     //but it seems to screw up the connection  or context ?
                     tls.close();
                     ctx.close();
                
                     System.out.println("Successfully created User: " + userName);
                
                } 
                catch (NamingException e) {
                     System.err.println("Problem creating object: " + e);
                }
           
                catch (IOException e) {
                     System.err.println("Problem creating object: " + e);               }
           }
      }
        • 1. Re: JNDI, Active Directory (Creating new users & demystifying userAccountContro
          843793
          I'm using LDAP to connect to Active Directory. I used your code example but I couldn't create the full user account. If I used the first half and created a disabled account, that worked fine but if I used the rest I got this stack trace:

          Created disabled account for: CN=two,OU=FatWire,DC=seedadev,DC=net
          NamingException - Problem creating object: javax.naming.CommunicationException:
          Encountered ASN.1 tag 48 (expected tag 10) [Root exception is com.sun.jndi.ldap.
          Ber$DecodeException: Encountered ASN.1 tag 48 (expected tag 10)]
          javax.naming.CommunicationException: Encountered ASN.1 tag 48 (expected tag 10)
          [Root exception is com.sun.jndi.ldap.Ber$DecodeException: Encountered ASN.1 tag
          48 (expected tag 10)]
          at com.sun.jndi.ldap.LdapCtx.extendedOperation(Unknown Source)
          at javax.naming.ldap.InitialLdapContext.extendedOperation(Unknown Source
          )
          at NewUser.main(NewUser.java:90)
          Caused by: com.sun.jndi.ldap.Ber$DecodeException: Encountered ASN.1 tag 48 (expe
          cted tag 10)
          at com.sun.jndi.ldap.BerDecoder.parseIntWithTag(Unknown Source)
          at com.sun.jndi.ldap.BerDecoder.parseEnumeration(Unknown Source)
          at com.sun.jndi.ldap.LdapClient.parseResult(Unknown Source)
          at com.sun.jndi.ldap.LdapClient.parseExtResponse(Unknown Source)
          at com.sun.jndi.ldap.LdapClient.extendedOp(Unknown Source)
          ... 3 more

          Any ideas??
          PS. I am able to connect to AD over ports 389 and 636 using SSL and edit user attributes etc with an LDAP browser but I can't create them with a password attribute.
          • 2. Re: JNDI, Active Directory (Creating new users & demystifying userAccountCo
            843793
            try to check the password policy of your Active Directory (in the machine policy and its domain controller policy) . hope this help
            • 3. Re: JNDI, Active Directory (Creating new users & demystifying userAccountCo
              843793
              Hi adler_steven,

              I get the same error .
              Problem creating object: javax.naming.CommunicationException: Encountered ASN.1 tag 48 (expected tag 10) [Root exception is com.sun.jndi.ldap.Ber$DecodeException: Encountered ASN.1 tag 48 (expected tag 10)]
              javax.naming.CommunicationException: Encountered ASN.1 tag 48 (expected tag 10) [Root exception is com.sun.jndi.ldap.Ber$DecodeException: Encountered ASN.1 tag 48 (expected tag 10)]
                   at com.sun.jndi.ldap.LdapCtx.extendedOperation(LdapCtx.java:3179)

              This was caused by the line (debugging):

              StartTlsResponse tls = (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());

              Any suggestions?

              Cheers,
              Peter
              • 4. Re: JNDI, Active Directory (Creating new users & demystifying userAccountCo
                800477
                I have not attempted to duplicate your error, however the error message is indicative of a communications error rather than a pure Active Directory error.

                (If it was an Active Directory error, you would get error messages similar to those described in
                http://forum.java.sun.com/thread.jspa?threadID=578674&tstart=50)

                I have only tested the sample code using JDK 1.4.0 and JDK 1.5.0. I do not know if there are any differences in the SSL/TLS configuration for your JRE or your system.

                Note that TLS is only supported against Windows Server 2003.

                TLS is not supported on Windows 2000 Server.

                SSL is supported on both platforms.

                If you cannot get TLS to work, then you could use SSL to pretect the entire LDAP session, and it would still allow you to set the users password.
                • 5. Re: JNDI, Active Directory (Creating new users & demystifying userAccountCo
                  843793
                  Ok, that's it. I ran the code against Win2000. Thanks for the clarification!

                  Peter
                  • 6. Re: JNDI, Active Directory (Creating new users & demystifying userAccountCo
                    843793
                    I'm tring to run your code example but I can't create a new user.

                    Doing :
                    Context result = ctx.createSubcontext(userName, attrs);
                    I got the exception : Problem creating object: javax.naming.directory.NoSuchAttributeException: [LDAP: error code 16 - 00000057: LdapErr: DSID-0C090A75, comment: Error in attribute conversion operation, data 0, vece_

                    I'm using Windows Server 2003 and j2sdk1.4.1, It's ok?

                    Can you help me?
                    Thank you so much                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
                    • 7. Re: JNDI, Active Directory (Creating new users & demystifying userAccountCo
                      800477
                      You have obviously changed something from the posted sample.

                      The error message that you are receiving "Error in attribute conversion operation" indicates that you are submitting a value for an attribute and it is not of the correct syntax.
                      • 8. Re: JNDI, Active Directory (Creating new users & demystifying userAccountCo
                        843793
                        Hi,
                        Has some1 solved the pb using SSL instead of TLS ?
                        Regards,
                        Tarun
                        • 9. Re: JNDI, Active Directory (Creating new users & demystifying userAccountCo
                          800477
                          I'm sure an enterprising developer such as yourself could refer to http://forum.java.sun.com/thread.jspa?threadID=581425&tstart=0 and make the necessary modifications in sample code listed in this topic, to remove the TLS code and add the appropriate SSL code.

                          About half a dozen lines of code in my estimate :-)
                          • 10. Re: JNDI, Active Directory (Creating new users & demystifying userAccountCo
                            843793
                            hi adler_steven,

                            i am as well interested in how to solve that problem with SSL instead of TLS. everything works fine with the code till i'm going to modify password and userAccountControl attribute. i introduced nothing till now - simply commented the TLS part and assume that this causes exception below:

                            javax.naming.OperationNotSupportedException: [LDAP: error code 53 - 00002077: SvcErr: DSID-031D0AAB, problem 5003 (WILL_NOT_PERFORM), data 0

                            will be happy if you can help me out of this problem as well :o)

                            thanx in advance

                            cu fledi                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
                            • 11. Re: JNDI, Active Directory (Creating new users & demystifying userAccountCo
                              843793
                              Steven,

                              The first half works fine. Creating the disabled user id. However I get the following error when the following statement is executed:

                              StartTlsResponse tls = (StartTlsResponse)ctx.extendedOperation(new StartTlsRequest());

                              Error message below:

                              Problem creating object: javax.naming.ServiceUnavailableException: [LDAP: error code 52 - 00000000: LdapErr: DSID-0C090C1F, comment: Error initializing SSL/TLS, data 0, vece ; remaining name ''


                              Thanks in advance,

                              Yeppo                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
                              • 12. Re: JNDI, Active Directory (Creating new users & demystifying userAccountCo
                                800477
                                I'm sorry I can't tell you off hand what wrong with your domain controller.

                                It seems like it isn't listening on the SSL port or TLS is not supported.

                                You may just want to use one of the Windows tools (such as LDP) to determine if you can connect using SSL (port 636) with your domain controller.

                                Also you may not have installed a certificate on your domain controller. Either install a Windows CA which will automatically enroll domain controller certificates or if you have a third party CA follow the instructions on http://support.microsoft.com/default.aspx?scid=kb;en-us;321051

                                Note that TLS is only supported on Windows Server2003. SSL is supported on both Windows Server2003 and Windows 2000

                                And for those who wanted a sample app that adds a users and sets their password over SSL (it really was only half a dozen lines of code)
                                /**
                                 * newssluser.java
                                 * 5 July 2001
                                 * December 2004 
                                 * Updated for Win2K3 Password complexity and to use TLS during password set
                                 * February 2005
                                 * Changed to use SSL instead of TLS
                                 *
                                 * Sample JNDI application that creates a new user in the Active Directory.
                                 * 
                                 */
                                
                                import java.util.Hashtable;
                                import javax.naming.*;
                                import javax.naming.ldap.*;
                                import javax.naming.directory.*;
                                import javax.net.ssl.*;
                                import java.io.*;
                                
                                public class newssluser
                                {
                                     public static void main (String[] args)
                                     {
                                     
                                          Hashtable env = new Hashtable();
                                          String adminName = "CN=Administrator,CN=Users,DC=antipodes,DC=com";
                                          String adminPassword = "XXXXXX";
                                          String ldapURL = "ldaps://mydc.antipodes.com:636";
                                          String userName = "CN=Benjamin Franklin,OU=Research,DC=antipodes,DC=com";
                                          String newPassword = "Password123";
                                          String groupName = "CN=All Research,OU=Research,DC=antipodes,DC=com";
                                          
                                          env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
                                     
                                          //Access the truststore 
                                          String keystore = "/usr/java/jdk1.5.0_01/jre/lib/security/cacerts";
                                          System.setProperty("javax.net.ssl.trustStore",keystore);
                                          
                                          //set security credentials, note using simple cleartext authentication
                                          env.put(Context.SECURITY_AUTHENTICATION,"simple");
                                          env.put(Context.SECURITY_PRINCIPAL,adminName);
                                          env.put(Context.SECURITY_CREDENTIALS,adminPassword);
                                
                                          //specify the use of SSL
                                          env.put(Context.SECURITY_PROTOCOL,"ssl");
                                                    
                                          //connect to my domain controller
                                          env.put(Context.PROVIDER_URL,ldapURL);
                                                    
                                          try {
                                
                                               // Create the initial directory context
                                               LdapContext ctx = new InitialLdapContext(env,null);
                                
                                               // Create attributes to be associated with the new user
                                                   Attributes attrs = new BasicAttributes(true); 
                                              
                                               //These are the mandatory attributes for a user object
                                               //Note that Win2K3 will automagically create a random 
                                               //samAccountName if it is not present. (Win2K does not)
                                               attrs.put("objectClass","user");
                                                   attrs.put("samAccountName","BenF");
                                               attrs.put("cn","Benjamin Franklin");
                                
                                               //These are some optional (but useful) attributes
                                               attrs.put("givenName","Benjamin");
                                               attrs.put("sn","Franklin");
                                               attrs.put("displayName","Benjamin Franklin");
                                               attrs.put("description","Kite Flying Research Scientist");
                                                   attrs.put("userPrincipalName","BenF@antipodes.com");
                                                   attrs.put("mail","Benjamin.Franklin@antipodes.com");
                                               attrs.put("telephoneNumber","1 800 FLY KITE");
                                               
                                               //some useful constants from lmaccess.h
                                               int UF_ACCOUNTDISABLE = 0x0002;
                                               int UF_PASSWD_NOTREQD = 0x0020;
                                               int UF_PASSWD_CANT_CHANGE = 0x0040;
                                               int UF_NORMAL_ACCOUNT = 0x0200;
                                               int UF_DONT_EXPIRE_PASSWD = 0x10000;
                                               int UF_PASSWORD_EXPIRED = 0x800000;
                                          
                                               //Note that you need to create the user object before you can
                                               //set the password. Therefore as the user is created with no 
                                               //password, user AccountControl must be set to the following
                                               //otherwise the Win2K3 password filter will return error 53
                                               //unwilling to perform.
                                
                                                   attrs.put("userAccountControl",Integer.toString(UF_NORMAL_ACCOUNT + UF_PASSWD_NOTREQD + UF_PASSWORD_EXPIRED+ UF_ACCOUNTDISABLE));
                                
                                          
                                               // Create the context
                                               Context result = ctx.createSubcontext(userName, attrs);
                                               System.out.println("Created disabled account for: " + userName);
                                
                                               //now that we've created the user object,
                                               //set the password and change the userAccountControl
                                          
                                               //set password is a ldap modfy operation
                                               //also update the userAccountControl
                                               //enabling the acount and forcing the user to 
                                               //update ther password the first time they login
                                               ModificationItem[] mods = new ModificationItem[2];
                                          
                                               //Replace the "unicdodePwd" attribute with a new value
                                               //Password must be both Unicode and a quoted string
                                               String newQuotedPassword = "\"" + newPassword + "\"";
                                               byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
                                
                                               mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword));
                                               mods[1] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("userAccountControl",Integer.toString(UF_NORMAL_ACCOUNT + UF_PASSWORD_EXPIRED)));
                                          
                                               // Perform the update
                                               ctx.modifyAttributes(userName, mods);
                                               System.out.println("Set password & updated userccountControl");
                                
                                               //now add the user to a group.
                                
                                                    try     {
                                                         ModificationItem member[] = new ModificationItem[1];
                                                         member[0]= new ModificationItem(DirContext.ADD_ATTRIBUTE, new BasicAttribute("member", userName)); 
                                                    
                                                         ctx.modifyAttributes(groupName,member);
                                                         System.out.println("Added user to group: " + groupName);
                                
                                                    } 
                                                    catch (NamingException e) {
                                                          System.err.println("Problem adding user to group: " + e);
                                                    }
                                
                                               ctx.close();
                                          
                                               System.out.println("Successfully created User: " + userName);
                                          
                                          } 
                                          catch (NamingException e) {
                                               System.err.println("Problem creating object: " + e);
                                          }
                                     
                                          catch (IOException e) {
                                               System.err.println("Problem creating object: " + e);               }
                                     }
                                }
                                • 13. Re: JNDI, Active Directory (Creating new users & demystifying userAccountCo
                                  843793
                                  Hi Steven,

                                  I did install a self signed certificate - created using OpenSSL on the domain controller . I also exported the certificate and installed it on my local machine, imported it into the cacerts file in jre/....
                                  Also the server is Windows 2003. How do I check if the serer has been enabled for TLS. I know SSL port is open .

                                  Thanks,

                                  Yeppo
                                  • 14. Re: JNDI, Active Directory (Creating new users & demystifying userAccountCo
                                    843793
                                    Incidentally I used an LDAP browser and was unable to connect via SSL to the DC.
                                    This is the error I get.

                                    2:45:36 PM: Failed to connect to ldap://xxdc1:636
                                    Root error: simple bind failed: xxdc1:636

                                    Is this what the LDP tool tries to do. Do I need to establish certificate validation for this too.

                                    Thks.

                                    Yeppo
                                    1 2 3 Previous Next