Active Directory UDF - General

From AutoIt Wiki
Jump to navigation Jump to search

The Active Directory UDF offers functions to control and manipulate Microsoft Active Directory. This page describes the Active Directory UDF in general.

Concept

The Active Directory UDF is based upon this concept:

  • Open a connection to the AD, do all the work then close the connection
  • Access one domain at a time. Cross domain scripts are not possible with this UDF
  • Access the Global Catalog to search the complete forest
  • Access to the Active Directory can be made secure using SSL for both the domain and the Global Catalog

More information can be found on MSDN.

Prerequisites

You have to run version 3.3.6.0 of AutoIt or newer.

Structure

Every script calls at least the following functions:

_AD_Open()      ; Open a connection to the Active Directory
_AD_*           ; Any function that queries or alters the Active Directory
_AD_Close()     ; Close the connection

Open a connection

There are multiple ways to open a connection to the Active Directory.
Only one connection can be open at any time. This means you can't connect to two domains at the same time. You have to connect to the first domain, do all the work, close the connection and open the connection to the second domain. This is true for the Global Catalog as well.

To current domain

No additional information is needed if you want to connect to the domain the computer is already connected to. _AD_Open uses the credentials of the currently logged in user to connect to the domain. If the currently logged in user doesn't have the required privileges you can specify the credentials of a different user.

Example:
Open an AD connection to the domain the computer has logged in and use the windows logon credentials:

Local $iResult = _AD_Open()

Example:
Open an AD connection to the domain the computer has logged in and specify the credentials to use. The UserID has to be in one of the following formats (assume samAccountName = DJ and domain name = microsoft):

Windows Login Name   e.g. "DJ"
NetBIOS Login Name   e.g. "microsoft\DJ"
User Principal Name  e.g. "DJ@microsoft.com"

The following examples are equivalent:

$iResult = _AD_Open("DJ", "password of DJ")
$iResult = _AD_Open("microsoft\DJ", "password of DJ")
$iResult = _AD_Open("DJ@microsoft.com", "password of DJ")

If you want to connect to a specific Domain Controller in the domain then simply specify the HostServer.

Example:

$iResult = _AD_Open("DJ", "password of DJ", "", "DC1.domain.com") ; setting alternate credentials
$iResult = _AD_Open("", "", "", "DC1.domain.com")                 ; using the windows login credentials

To another domain

Much more information is needed if you want to connect to a different domain than the computer is already connected to. You have to specify DNSDomain, HostServer and Configuration. If you don't specify alternate credentials then the credentials of the currently logged in windows user are used.

Examples of the needed parameters:

$sDNSDomainParam     = Active Directory domain name e.g. "DC=subdomain,DC=example,DC=com"
$sHostServerParam    = Name of Domain Controller    e.g. "servername.subdomain.example.com" or "subdomain.example.com" (access the domain root)
$sConfigurationParam = Configuration naming context e.g. "CN=Configuration,DC=subdomain,DC=example,DC=com"

Example:
Open an AD connection to another domain and use the windows logon credentials:

Local $iResult = _AD_Open("", "", "DC=subdomain,DC=example,DC=com", "servername.subdomain.example.com", "CN=Configuration,DC=subdomain,DC=example,DC=com")

Example:
Open an AD connection to another domain and specify alternate credentials:

Local $iResult = _AD_Open("DJ", "password of DJ", "DC=subdomain,DC=example,DC=com", "servername.subdomain.example.com", "CN=Configuration,DC=subdomain,DC=example,DC=com")

More information on how to specify credentials can be found here.

From a workgroup

If your computer is not connected to any domain you have to provide the same information as if connecting to another domain. How to connect to another domain is described here.

To a Global Catalog

If you want to connect to a Global Catalog - so the search functions work on the whole forest and not just on a single domain - you have to append port number 3268 to the HostServer parameter.

Example:

Local $iResult = _AD_Open("", "", "", "DC1.company.com:3268")

All functions now access the Global Catalog. Every search function now returns results for the whole forest not just the domain.
In a forest there can be multiple Global Catalogs. To get a list of Global Catalogs connect to the domain and use _AD_ListDomainControllers.

Local $iResult = _AD_Open()
Local $aDCs = _AD_ListDomainControllers()
For $iIndex = 1 to $aDCs[0][0]
   If $aDCs[$iIndex][6] = True Then ConsoleWrite("DC " & $aDCs[$iIndex][0] & " is a Global Catalog")
Next
_AD_Close()

Secure connection

To a Domain Controller

If you want to use SSL and/or password encryption when you connect to a Domain Controller you have to set parameter 6 of _AD_Open.
Valid entries are:

1 = Sets the connection property "Encrypt Password" to True to encrypt userid and password
2 = The channel is encrypted using Secure Sockets Layer (SSL). AD requires that the Certificate Server be installed to support SSL
3 = Combination of 1 and 2

Example:

Local $iResult = _AD_Open("", "", "", "", "", 2)    ; Uses SSL to connect to a Domain Controller of the current domain

To a Global Catalog

If you want to use SSL for your connection to the Global Catalog use port 3269 instead of 3268.

Set security settings from outside your script

Microsoft has changed the minimum security requirements for LDAP access as described here.

If you do not want to modify all of your AutoIt scripts you can modify the security setting from outside. The UDF provides several levels to set the security from outside. The levels are processed from low to high, overwriting a previously set level.

  1. AD.INI file in the AutoIt include directory with a GLOBAL section (this is used for ALL scripts. It's a Global setting)
  2. AD.INI file in the AutoIt include directory with a <scriptname> section (this is used for scripts named <scriptname>.au3 or <scriptname>.exe. It's a Global setting)
  3. AD.INI file in the working directory with a GLOBAL section name (this is used for ALL scripts in the working directory. It's a Local setting)
  4. AD.INI file in the working directory directory with a <scriptname> section name (this is used for scripts named <scriptname>.au3 or <scriptname>.exe in the working directory. It's a Local setting)
  5. Set Global variable $__iSecurity in your script or one of your include files

Example of an INI-file:

[Global]
Security=1

[TEST_Open]
Security=18

The Global variable $__iSecurity or the SECURITY key from the INI files can have the values as specified for the $iSecurity parameter.
If $iSecurity was not specified in _AD_Open then this Global values overwrite $iSecurity.
If $iSecurity was specified in _AD_Open then you need to add 16 to this Global values to overwrite $iSecurity.

This means for the INI file above:

  • If you didn't specify security setting for _AD_Open then security is set to 1 (encrypt userid and password) else the security setting for _AD_Open remains unchanged.
    This is True for all scripts but _TEST_Open.
  • The security setting for _AD_Open gets overwritten with a value of 2 (encrypt the channel using Secure Sockets Layer (SSL)). This is because you added 16 to the security setting.
    This is True for _TEST_Open.

This rules apply to the Global variable $__iSecurity as well.

Error handling

_AD_Open sets the return value to 0 and @error plus @extended to denote what kind of error occurred.
On Windows Vista and later - if you specified the UserID as NetBIOS Login Name or User Principal Name - you can get additional information about the error. _AD_GetlastADSIError will return an array of additional information:

Local $iResult = _AD_Open()
If @error <> 0 Then
   Local $aError = _AD_GetlastADSIError()
   _ArrayDisplay($aError, "Error occurred when running _AD_Open")
   Exit
EndIf

This could look like:

[0] 5 
[1] 2148074248                                                                              - ADSI error code (decimal)
[2] 80090308: LdapErr: DSID-0C0903A9, comment: AcceptSecurityContext error, data 52e, v1db0 - Unicode string that describes the error
[3] LDAP Provider                                                                           - name of the provider that raised the error
[4] 52e                                                                                     - Win32 error code extracted from element[2]
[5] Logon failure: unknown user name or bad password                                        - description of the Win32 error code

Some of the Win32 error codes you can see:

525 = user not found
52e = invalid credentials
530 = not permitted to logon at this time
532 = password expired
533 = account disabled
701 = account expired
773 = user must reset password

Fine-Grained Password Policy

The UDF does not fully support the Fine-Grained Password Policy (FGPP). The following functions have been checked how they support the FGPP:

  • _AD_IsPasswordExpired (supports FGPP)
  • _AD_GetPasswordDontExpire (not affected)
  • _AD_SetPassword (not affected)
  • _AD_ChangePassword (not affected)
  • _AD_DisablePasswordExpire (not affected)
  • _AD_EnablePasswordExpire (not affected)
  • _AD_EnablePasswordChange (not affected)
  • _AD_DisablePasswordChange (not affected)
  • _AD_SetPasswordExpire (not affected)


The following functions do not (fully) support FGPP:

  • _AD_GetPasswordExpired (does not support FGPP)
  • _AD_GetPasswordInfo (all returned values are based on the Group Policy, except value 13 (calculated password expiration date/time) which takes FGPP into account)
  • _AD_IsObjectLocked (does not support FGPP)

Debugging

_AD_ErrorNotify

To get detailed error information about COM errors add function _AD_ErrorNotify. This function traps all COM errors and logs them. The following values can be used as first parameter:

1 = Uses ConsoleWrite to display the COM error message. Therefore doesn't work for compiled scripts
2 = Uses MsgBox to display the COM error message
3 = Uses FileWrite to write (append) the COM error message to a defined file
4 = Enable Debugging. The COM errors will be handled (the script no longer crashes) but without any output

The second parameter sets the output file. Default = @ScriptDir & "\AD_Debug.txt".

The following example parses the commandline and sets debugging to "MsgBox" when command line parameter one is set to "/debug" or "-debug".

If $CmdLine[0] And ($CmdLine[1] = "/debug" Or $CmdLine[1] = "-debug") Then _AD_ErrorNotify(2)

_AD_Open

When _AD_Open returns @error = 4 and @extended = -2147023541 (decimal) = 0x8007054b (hex) which translates to "The specified domain either does not exist or could not be contacted." then

  • most of the time the reason is that you try to connect using a local account. To solve the issue logon with a domain account or provide the needed parameters to _AD_Open.
  • you didn't save your compiled script to a "secure location". Secure locations are defined by group policies. I suggest to copy the exe to another directory and try again or ask your sysadmin.
  • if you run a script compiled for 32 bit on a 64 Windows and SysWOW64 redirection is disabled then you will see the same error. Please check: https://www.autoitscript.com/forum/topic/106163-active-directory-udf/?do=findComment&comment=1327001

Running queries

There are many functions in the UDF that allow to query the Active Directory. The most important functions are:

  • _AD_GetObjectsInOU: This function uses LDAP to query objects (users, computers ...) in the whole domain or a subtree.
  • _AD_GetObjectProperties: This function returns all or just the specified properties of an object in a "decoded" (readable form).

Example scripts

Query Bitlocker Recovery Information

; Class msFVE-RecoveryInformation: https://docs.microsoft.com/en-us/windows/desktop/ADSchema/c-msfve-recoveryinformation
; Delegating access in AD to BitLocker recovery information: https://blogs.technet.microsoft.com/craigf/2011/01/26/delegating-access-in-ad-to-bitlocker-recovery-information/

#include <ad.au3>
_AD_Open()
$sAD_OU = _AD_SamAccountNameToFQDN(@ComputerName & "$")
$aResult = _AD_GetObjectsInOU($sAD_OU, "(objectcategory=msFVE-RecoveryInformation)", 2, "distinguishedname")
If @error <> 0 Then Exit MsgBox(64, "AD Test", "_AD_GetObjectsInOU @error: " & @error & ", @extended: " & @extended)
_ArrayDisplay($aResult, "Result of _AD_GetObjectsInOU")
$aResult = _AD_GetObjectProperties($aResult[1])
If @error <> 0 Then Exit MsgBox(64, "AD Test", "_AD_GetObjectProperties @error: " & @error & ", @extended: " & @extended)
_ArrayDisplay($aResult, "Result of _AD_GetObjectProperties")
_AD_Close()

Tips & Tricks

  • The UDF does not fully support the "granular password policy" feature implemented with Windows Server 2008. Please check the section about Fine-Grained Password Policy above.
  • Keyword "Default": You can't use the keyword "Default" to omit parameters in a function call as the UDF does not support this keyword (Edit: The UDF supports Default for versions > 1.4.7.0)
  • If you run Windows Vista or higher, UAC is enabled and you use functions that change the AD then you might need to insert #RequireAdmin into your script
  • The SamAccountName of a computer is the computername with a trailing "$" e.g. @ComputerName & "$"
  • Special characters: If you call a function with a Fully Qualified Domain Name (FQDN) as parameter you as a user have to make sure that all special characters ("\/#,+<>;=) are escaped with a preceding backslash. You can call function _AD_FixSpecialChars(String, 1) to escape or _AD_FixSpecialChars(string, 0) to unescape the special characters
  • How to run the AD UDF on Windows PE is described here.

Good reading