Using LDIF files and OpenLDAP tools
© 2009 Dennis Leeuw
This document not only documents how to create accounts within Active Directory with the use of Open Source tools (mainly the OpenLDAP tools), but is also a place where I store everything that I found during my search for the knowledge required to create such accounts.
Microsoft can be best accessed through secure access. To accomplish this one should create a .ldaprc file in ones home directory with the following content:
use_sasl on ssl on sasl start_tls SASL_MECH GSSAPI tls_checkpeer no tls_ciphers TLSv1 TLS_REQCERT never chasereferrals yes deref always uri uri ldaps://ads.forest.example.com/ binddn cn=administrator,cn=Users,dc=forest,dc=example,dc=com # Tell GSSAPI not to negotiate a security or privacy layer since # AD doesn't support nested security or privacy layers sasl_secprops minssf=0,maxssf=0 tls_cacertfile ads.pem"
Microsoft stores passwords as a simple base64 encoded quoted string, thus the only way to set the password in a secure manner is by using an SSL encrypted connection, which Microsoft thus enforces on the connection to set the password. To make this possible, you need the server certificate from AD. The ads.pem file can be obtained from the AD server like this:
Click on: Download a CA certificate, certificate chain, or CRL
Click on: Download CA certificate
Click on: View
Select the details tab, and click on Export
Save the pem file like: ads.pem
And use ldapadd or ldapmodify like this:
ldapadd -x -H ldaps://ads.forest.example.com:636/ \ -D "CN=Administrator,CN=Users,DC=forest,DC=example,DC=com" -W -f file.ldif
dn: CN=MyGroup,cn=Groups,dc=forest,dc=examle,dc=com name: MyGroup distinguishedName: CN=MyGroup,cn=Groups,dc=forest,dc=examle,dc=com instanceType: 4 sAMAccountName: MyGroup msSFU30Name: MyGroup msSFU30NisDomain: forest gidNumber: 666
Note: The NisDomain is needed to set the gidNumber. The gidNumber is not needed by AD, but is used for Unix interoperability.
A fixed value used in all LDIF account files is the instanceType, 4 means that the object is writable on this directory:
|0x00000001||The head of naming context|
|0x00000002||This replica is not instantiated|
|0x00000004||The object is writable on this directory|
|0x00000008||The naming context above this one on this directory is held|
|0x00000010||The naming context is in the process of being constructed for the first time via replication|
|0x00000020||The naming context is in the process of being removed from the local DSA|
In LDIF files taken from AD you see a lot of extra fields. Setting one of those fields will most of the time make the server "unwilling to perform". Still I'd like to give some explanation about some of the fields so you know what they mean.
|Internal name||Hex value||LDIF value|
To create users from an LDIF you first need to create the user, and make it disabled, then set the password and then enable the account. However we can use a single LDIF file to do all those steps at once.
The userAccountControl determines if an account is enabled or disabled. According to Microsoft's documentation the following values can be used and combined:
|PASSWD_NOTREQD||32||You can not assign this permission|
To make sure that the account never expires we set the accountExpires value to 0, which seems to work. Although according to http://arnoutvandervorst.blogspot.com/2008/03/ldap-accountexpires-attribute-values.html, the initial value should be: 9223372036854775807.
Of course the user needs a password. To create the password do:
echo -n "\"password\"" | iconv -f UTF8 -t UTF16LE | base64 -w 0
Microsoft stores a quoted password in little endian UTF16 base64 encoded. The trivial command above takes care of it all. Note the -n option to echo, otherwise the carriage-return will also be part of the password.
The first part of the following LDIF creates the disabled user account, the second part sets the password and the last part enables the account:
dn: CN=Piet Prutser,CN=Users,DC=forest,DC=example,DC=com changetype: add objectClass: top objectClass: person objectClass: organizationalPerson objectClass: user objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=example,DC=com codePage: 0 countryCode: 0 distinguishedName: CN=Piet Prutser,CN=Users,DC=forest,DC=example,DC=com cn: Piet Prutser sn: Prutser givenName: Piet displayName: Piet Prutser name: Piet Prutser telephoneNumber: 123456 instanceType: 4 userAccountControl: 514 accountExpires: 0 uidNumber: 600 gidNumber: 600 sAMAccountName: pprutser userPrincipalName: P.Prutser@example.com altSecurityIdentities: Kerberos:pprutser@EXAMLE.COM mail: P.Prutser@example.com homeDirectory: \\ads\home\pprutser homeDrive: Z: unixHomeDirectory: /home/pprutser loginShell: /bin/bash dn: CN=Piet Prutser,OU=Users,DC=forest,DC=example,DC=com changetype: modify replace: unicodePwd unicodePwd::IlwwdFwwZVwwc1wwdFwwIgo= dn: CN=Piet Prutser,OU=Users,DC=forest,DC=example,DC=com changetype: modify replace: userAccountControl userAccountControl: 512
When you get an error report like this:
ldap_modify: Server is unwilling to perform (53) additional info: 0000001F: SvcErr: DSID-031A0FC0, problem 5003 (WILL_NOT_PERFORM), data 0It means that the password (like the one in the example) is not a correct UTF16LE password (you didn't think I would actually put in a correct password with such weak encryption didn't you).
dn: CN=HOSTNAME,ou=Computers,dc=forest,dc=example,dc=com changetype: add objectClass: top objectClass: person objectClass: organizationalPerson objectClass: user objectClass: computer cn: HOSTNAME distinguishedName: CN=HOSTNAME,ou=Computers,dc=forest,dc=example,dc=com objectCategory: CN=Computer,CN=Schema,CN=Configuration,dc=forest,dc=example,dc=com instanceType: 4 displayName: HOSTNAME$ name: HOSTNAME userAccountControl: 4096 codePage: 0 countryCode: 0 accountExpires: 0 sAMAccountName: HOSTNAME$ dNSHostName: HOSTNAME.forest.example.com servicePrincipalName: HOST/HOSTNAME servicePrincipalName: HOST/HOSTNAME.forest.example.com
Fields that certainly will not be excepted are:
isCriticalSystemObject: localPolicyFlags: sAMAccountType:A special one which is also not accepted is primaryGroupID. Somehow Microsoft uses some predefined Group IDs:
Within AD you have several places where lists of users are maintained who belong to a certain group. The user properties has a 'Member Of' tab and the group properties has a 'Member Of' and a 'Members' tab. The 'Member Of' tabs are not changeable. They only show to which group an object (group or user) belongs. The Members list is what we are concerned with. This one you can change. A simple LDIF like this:
dn: CN=groupname,ou=Groups,dc=forest,dc=example,dc=com changetype: modify add: member member: CN=User Name,CN=Users,DC=forest,DC=example,DC=comadds the use with user name 'User Name' to the group 'groupname'.