1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

howto decode Active Directory attribute UserAccountControl

Discussion in 'T&T - Miscellaneous' started by Frank, Aug 26, 2012.

  1. Frank

    Joined:
    Aug 2, 2011
    Messages:
    258
    Likes Received:
    4
    hello programmers,
    I know there are several tools to extract informations from AD,
    but I ask myself if I could decode the attribute UserAccountControl with TCC in-house means.
    If I use ADFIND I get integer values as described here.
    Code:
    >adfind -f "objectcategory=person" -b ou=dv,ou=!ber,dc=company,dc=de useraccountcontrol -csv
    "dn","useraccountcontrol"
    "CN=test\, sap,OU=DV,OU=!BER,DC=company,DC=de","514"    (NORMAL_ACCOUNT + disabled)
    "CN=Berlin\, Willi,OU=Users,OU=DV,OU=!BER,DC=company,DC=de","66048"    (NORMAL_ACCOUNT + DONT_EXPIRE_PASSWORD)
    "CN=xxxxxxxxx\, Frank,OU=Users,OU=DV,OU=!BER,DC=company,DC=de","512"    (NORMAL_ACCOUNT)
    "CN=Test-User\, Tony,OU=people,OU=DV,OU=!BER,DC=company,DC=de","514"    (NORMAL_ACCOUNT + disabled)
    "CN=pwdtest\, frank,OU=DV,OU=!BER,DC=company,DC=de","514"      (NORMAL_ACCOUNT + disabled)
    
    I would like to do something like this:
    Code:
    do      account in @adfind.out
            set    dn=%@word[",",0,%account]
            set    uac=%@unquote[%@word[",",1,%account]]
            gosub  check-uac
           echo%dn:%@if[PASSWD_NOTREQD eq 1,yes,no],%@if[ACCOUNTDISABLE eq 1,yes,no]...
    enddo
    
    This is a rough-and-ready example. I just want to create a list of accounts with special attributes (set or not set).
    The challenge is to decode the integer field into the property flags.
    Any help would be appreciated.

    btw:
    just listening to "Dean Brown, Here" :cool:
     
  2. JohnQSmith

    Joined:
    Jan 19, 2011
    Messages:
    559
    Likes Received:
    7
  3. JohnQSmith

    Joined:
    Jan 19, 2011
    Messages:
    559
    Likes Received:
    7
    Code:
    @ECHO OFF
    SET IntegerField=100613115
    IFF %IntegerField GE 67108864 THEN
        ECHO PARTIAL_SECRETS_ACCOUNT
        SET IntegerField=%@EVAL[%IntegerField-67108864]
    ENDIFF
    IFF %IntegerField GE 16777216 THEN
        ECHO TRUSTED_TO_AUTH_FOR_DELEGATION
        SET IntegerField=%@EVAL[%IntegerField-16777216]
    ENDIFF
    IFF %IntegerField GE 8388608 THEN
        ECHO PASSWORD_EXPIRED
        SET IntegerField=%@EVAL[%IntegerField-8388608]
    ENDIFF
    IFF %IntegerField GE 4194304 THEN
        ECHO DONT_REQ_PREAUTH
        SET IntegerField=%@EVAL[%IntegerField-4194304]
    ENDIFF
    IFF %IntegerField GE 2097152 THEN
        ECHO USE_DES_KEY_ONLY
        SET IntegerField=%@EVAL[%IntegerField-2097152]
    ENDIFF
    IFF %IntegerField GE 1048576 THEN
        ECHO NOT_DELEGATED
        SET IntegerField=%@EVAL[%IntegerField-1048576]
    ENDIFF
    IFF %IntegerField GE 524288 THEN
        ECHO TRUSTED_FOR_DELEGATION
        SET IntegerField=%@EVAL[%IntegerField-524288]
    ENDIFF
    IFF %IntegerField GE 262144 THEN
        ECHO SMARTCARD_REQUIRED
        SET IntegerField=%@EVAL[%IntegerField-262144]
    ENDIFF
    IFF %IntegerField GE 131072 THEN
        ECHO MNS_LOGON_ACCOUNT
        SET IntegerField=%@EVAL[%IntegerField-131072]
    ENDIFF
    IFF %IntegerField GE 65536 THEN
        ECHO DONT_EXPIRE_PASSWORD
        SET IntegerField=%@EVAL[%IntegerField-65536]
    ENDIFF
    IFF %IntegerField GE 8192 THEN
        ECHO SERVER_TRUST_ACCOUNT
        SET IntegerField=%@EVAL[%IntegerField-8192]
    ENDIFF
    IFF %IntegerField GE 4096 THEN
        ECHO WORKSTATION_TRUST_ACCOUNT
        SET IntegerField=%@EVAL[%IntegerField-4096]
    ENDIFF
    IFF %IntegerField GE 2048 THEN
        ECHO INTERDOMAIN_TRUST_ACCOUNT
        SET IntegerField=%@EVAL[%IntegerField-2048]
    ENDIFF
    IFF %IntegerField GE 512 THEN
        ECHO NORMAL_ACCOUNT
        SET IntegerField=%@EVAL[%IntegerField-512]
    ENDIFF
    IFF %IntegerField GE 256 THEN
        ECHO TEMP_DUPLICATE_ACCOUNT
        SET IntegerField=%@EVAL[%IntegerField-256]
    ENDIFF
    IFF %IntegerField GE 128 THEN
        ECHO ENCRYPTED_TEXT_PWD_ALLOWED
        SET IntegerField=%@EVAL[%IntegerField-128]
    ENDIFF
    IFF %IntegerField GE 64 THEN
        ECHO PASSWD_CANT_CHANGE
        SET IntegerField=%@EVAL[%IntegerField-64]
    ENDIFF
    IFF %IntegerField GE 32 THEN
        ECHO PASSWD_NOTREQD
        SET IntegerField=%@EVAL[%IntegerField-32]
    ENDIFF
    IFF %IntegerField GE 16 THEN
        ECHO LOCKOUT
        SET IntegerField=%@EVAL[%IntegerField-16]
    ENDIFF
    IFF %IntegerField GE 8 THEN
        ECHO HOMEDIR_REQUIRED
        SET IntegerField=%@EVAL[%IntegerField-8]
    ENDIFF
    IFF %IntegerField GE 2 THEN
        ECHO ACCOUNTDISABLE
        SET IntegerField=%@EVAL[%IntegerField-2]
    ENDIFF
    IFF %IntegerField GE 1 THEN
        ECHO SCRIPT
        SET IntegerField=%@EVAL[%IntegerField-1]
    ENDIFF
    ECHO %IntegerField: should be zero now.
    
     
  4. Frank

    Joined:
    Aug 2, 2011
    Messages:
    258
    Likes Received:
    4
    Hello John,

    thank you for the help! That's cool.
    I was thinking much too complicated with bit-comparision or something like that.
    I think your solution is lean, clean and easy to understand. Thank you again.

    regards
    Frank
     
  5. Steve Fabian

    Joined:
    May 20, 2008
    Messages:
    3,520
    Likes Received:
    4
    Here is a version that does bit comparisons and uses a stored array to report the names of flags that are set:
    Code:
    @echo off
    iff %@arrayinfo[active_dir_flags,0] LT 0 then
      setarray active_dir_flags[32]
      set active_dir_flags[26]=PARTIAL_SECRETS_ACCOUNT
      set active_dir_flags[24]=TRUSTED_TO_AUTH_FOR_DELEGATION
      set active_dir_flags[23]=PASSWORD_EXPIRED
      set active_dir_flags[22]=DONT_REQ_PREAUTH
      set active_dir_flags[21]=USE_DES_KEY_ONLY
      set active_dir_flags[20]=NOT_DELEGATED
      set active_dir_flags[19]=TRUSTED_FOR_DELEGATION
      set active_dir_flags[18]=SMARTCARD_REQUIRED
      set active_dir_flags[17]=MNS_LOGON_ACCOUNT
      set active_dir_flags[16]=DONT_EXPIRE_PASSWORD
      set active_dir_flags[13]=SERVER_TRUST_ACCOUNT
      set active_dir_flags[12]=WORKSTATION_TRUST_ACCOUNT
      set active_dir_flags[11]=INTERDOMAIN_TRUST_ACCOUNT
      set active_dir_flags[9]=NORMAL_ACCOUNT
      set active_dir_flags[8]=TEMP_DUPLICATE_ACCOUNT
      set active_dir_flags[7]=ENCRYPTED_TEXT_PWD_ALLOWED
      set active_dir_flags[6]=PASSWD_CANT_CHANGE
      set active_dir_flags[5]=PASSWD_NOTREQD
      set active_dir_flags[4]=LOCKOUT
      set active_dir_flags[3]=HOMEDIR_REQUIRED
      set active_dir_flags[1]=ACCOUNTDISABLE
      set active_dir_flags[0]=SCRIPT
    endiff
    set /qa mask=0x8000000
    do n = 31 to 0 by -1
      set v=%active_dir_flags[%n]
      if defined v if %@eval[ %mask & %1] GT 0 echo %v
      set /qa mask=%mask SHR 1
    enddo
    
    Notes:
    1/ If the above is stored as a batchfile ACTIVE_FLAGS.BTM, invoke it as
    call ACTIVE_FLAGS flag_word
    2/ It makes the array name ACTIVE_DIR_FLAGS reserved (to avoid multiple initializations). One could instead UNSETARRAY ACTIVE_DIR_FLAGS at the end.
    3/ the "set v..." statement is used because "if defined array[index]" is FALSE even if the array element is initialized to a non-empty string. Alternately one could use the single statement:
    if "%active_dir_flags[%n]" NE "" if %@eval[ %mask & %1] GT 0 echo %active_dir_flags[%n]
    In both forms I use "if test1 if test2 command" to avoid evaluating test2 if test1 is FALSE. Combining the tests with .and. would result in evaluating test2 regardless of the value of test1, which - though harmless in this instance - often results in error when test1 is FALSE.
     
  6. Frank

    Joined:
    Aug 2, 2011
    Messages:
    258
    Likes Received:
    4
    Wow, you're using functions that I've noticed in the doc eventually but never really thought about their meaning.
    Now that you gave me such an excellent example, I will try to occupy myself with them.
    Thank you.
     
  7. Frank

    Joined:
    Aug 2, 2011
    Messages:
    258
    Likes Received:
    4
    Hi Steve,

    please give me a private lesson :)

    I (try to) understand your example as following:
    • you're filling an array with 27 (of max. 32) strings
    • you define a bit-mask with 29 bits which is sufficient for the 27 flags and the highest bit is set to on ?
    • instead of using 0x8000000 I could also use 0x80000000 (32 bits) ?
    • now we do a loop to check all bits from left/highest to right/lowest ?
    • if we have a valid array element at all, the bit is checked with "AND" and if set the string is echoed ?
    • at the end of each iteration we shift the bit to the right ?
      But I don't really understand the doc for "@eval SHR":
      "arithmetic right shift of the first parameter, truncated toward zero to an integer, by the number of bits specified by the second parameter"
    Please notice the ? , because I just try to understand.
    And please give me an advice if I am wrong.
    Thank you in advance.
     
  8. Steve Fabian

    Joined:
    May 20, 2008
    Messages:
    3,520
    Likes Received:
    4
    yes

    there is a copying error here - the last 0 was lost somewhere, the starting mask is supposed to be 2^31

    yes
    yes (bits for which there is no initialized array element are not significant and thus ignored)
    yes
    • But I don't really understand the doc for "@eval SHR":
      "arithmetic right shift of the first parameter, truncated toward zero to an integer, by the number of bits specified by the second parameter"[/quote]
    "the first parameter, truncated toward zero to an integer" means that if the first parameter is not an integer, its fractional part is discarded, so the absolute value of the parameter becomes smaller - it becomes closer to zero; the resulting value used. "shifting by the number of bits" implies the value of the parameter is represented in binary, and the bit pattern is shifted to the right - commonly understood to mean toward smaller values. "Arithmetic shift" is NOT defined, but is commonly understood to mean that the binary representation is conisdered to be in 2's complement mode, so the MSB is the sign, 1 for negative and 0 for positive values. In an "arithmetic right shift" under these conditions the MSB is copied into the 2nd MSB for each bit by which the value is shifted. In effect each bit by which the parameter is shifted divides it by 2, shifting by "n" bits divides it by 2^n. The least defined issue is the size of the word for the arithmetic shift. On my 32-bit system running WinXP SP3 home, @EVAL uses 64-bit integers, so the operation is correct.
    --
    HTH, Steve
     
  9. Frank

    Joined:
    Aug 2, 2011
    Messages:
    258
    Likes Received:
    4
    Yes, this helped me very much (although I will have to read the explanation even some more times).
    Thank you.
     

Share This Page