Windows のアクセス許可設定の要と言えば、ACL (= Access Control List) です。これについては、@IT で特集が組まれていて、詳しく、かつ分かりやすい記事になっています。

http://www.atmarkit.co.jp/fwin2k/win2ktips/700whatisacl/whatisacl.html

この ACL に実際に出会うときというのは、レジストリに書かれたバイナリだったり、SDDL 文字列だったりするわけですが、プログラム上では、SECURITY_DESCRIPTOR 構造体として出会うことが多いです。

typedef struct _SECURITY_DESCRIPTOR { 
   BYTE  Revision; 
   BYTE  Sbz1; 
   SECURITY_DESCRIPTOR_CONTROL Control; 
   PSID Owner; 
   PSID Group; 
   PACL Sacl; 
   PACL Dacl;

} SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR; 


typedef PVOID PSECURITY_DESCRIPTOR;

ポインタの定義が PVOID になっているところがミソです。ポインタから構造体のメンバーに直接アクセスすることはなく、GetSecurityDescriptor 何ちゃらという API を使って値を取り出します。おそらく、SECURITY_DESCRIPTOR 構造体がバージョンによって大きく仕様変更される可能性があるからでしょう。

多くのアプリケーションは、AccessCheckAndAuditAlarm 関数を使って、この SECURITY_DESCRIPTOR とアクセス権限を照合しています。ここにブレークポイントを置いてデバッグすると幸せになれるときがあるかも。
http://msdn.microsoft.com/en-us/library/aa374823(v=vs.85).aspx

SECURITY_DESCRIPTOR に含まれる SID や ACL は、以下のような微妙な定義のポインタがあるだけで、直接値を見ることはできません。これはバージョン間の互換性というよりは、SID や ACL の長さが可変であることが理由かと思います。

typedef PVOID PSID;

typedef struct _ACL { 
    BYTE  AclRevision; 
    BYTE  Sbz1;// パディング 
    WORD   AclSize; 
    WORD   AceCount; 
    WORD   Sbz2; // パディング 
    // ヘッダーのみで ACE は含まれていない 
} ACL; 
typedef ACL *PACL; 
そんなこんなで、SECURITY_DESRIPTOR, ACL (SACL DACL), ACE などの登場人物は扱いにくいというイメージが定着しています。この得体の知れない SECURITY_DESRIPTOR を人間が読めるようにしたのが SDDL 文字列だったりするわけですが、フラグを全部暗記するのは大変です。大体の構造は覚えておいたほうがいいと思いますけどね。

SDDL 文字列は、身近なところでは cacls コマンドで見ることができます。

c:\Windows\System32>cacls advapi32.dll /s 
c:\Windows\System32\advapi32.dll "D:PAI(A;;FA;;;S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464)(A;;0x1200a9;;;BA)(A;;0x1200a9;;;SY)(A;;0x1200a9;;;BU)"

SDDL フォーマットは実際単純で、ここに全部書いてあります。
http://msdn.microsoft.com/en-us/library/aa379570.aspx

今回作ったツールは、日々の業務で利用頻度が高いわりに、調べると意外と簡単にできない以下の操作が可能です。

  • 任意のユーザー (グループ) アカウントと SID の相互変換
  • SDDL の解析

前者のツールは数多くあるようですが、SDDL の解析ツールは出回っていないような気がします。要するにデバッガーの !sd コマンドです。

基本的には API を呼ぶだけなので、アルゴリズム的にトリッキーなところはありません。アカウントと SID の変換は LookupAccountSid と LookupAccountName を使うだけですし、SDDL 関連は、ConvertStringSecurityDescriptorToSecurityDescriptor を呼んで SECURITY_DESCRIPTOR を取得してから、GetSecurityDescriptor~ を呼ぶだけです。ただ、ACE が種類によって異なる構造になっているので厄介です。

ソースを貼る前に、出力結果を載せておきます。SDDL に関しては、以下の MSDN のサンプルと基本的に同じになるように作ってあります。
http://msdn.microsoft.com/en-us/library/aa379570.aspx

> acehack -sddl O:BAG:BAD:(A;;RPWPCCDCLCRCWOWDSDSW;;;SY)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(A;;RPLCRC;;;AU)S:(AU;SAFA;WDWOSDWPCCDCSW;;;WD)

O:BAG:BAD:(A;;RPWPCCDCLCRCWOWDSDSW;;;SY)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(A;;RPLCRC;;;AU)S:(AU;SAFA;WDWOSDWPCCDCSW;;;WD) 
Revision:     0x00000001 
Control:      0x8014 
                  SE_DACL_PRESENT 
                  SE_SACL_PRESENT 
                  SE_SELF_RELATIVE 
RMControl:    0x00 
Owner:        S-1-5-32-544 
PrimaryGroup: S-1-5-32-544

DACL 
    Revision: 0x04 
    Size:     0x005c 
    AceCount: 0x0003 
    Ace[0] 
        AceType:       0x00 (ACCESS_ALLOWED_ACE_TYPE) 
        AceFlags:      0x00 
        AceSize:       0x0014 
        Access Mask:   0x000f003f 
                            DELETE 
                            READ_CONTROL 
                            WRITE_DAC 
                            WRITE_OWNER 
                            Others(0x0000003f) 
        Ace Sid:       S-1-5-18 
    Ace[1] 
        AceType:       0x05 (ACCESS_ALLOWED_OBJECT_ACE_TYPE) 
        AceFlags:      0x00 
        AceSize:       0x002c 
        Access Mask:   0x00000003 
                            ADS_RIGHT_DS_CREATE_CHILD 
                            ADS_RIGHT_DS_DELETE_CHILD 
        Access Flags:  0x00000001 (ACE_OBJECT_TYPE_PRESENT) 
        ObjectType:    {bf967aa8-0de6-11d0-a285-00aa003049e2} 
        InhObjectType: Not defined 
        Ace Sid:       S-1-5-32-550 
    Ace[2] 
        AceType:       0x00 (ACCESS_ALLOWED_ACE_TYPE) 
        AceFlags:      0x00 
        AceSize:       0x0014 
        Access Mask:   0x00020014 
                            READ_CONTROL 
                            Others(0x00000014) 
        Ace Sid:       S-1-5-11

SACL 
    Revision: 0x02 
    Size:     0x001c 
    AceCount: 0x0001 
    Ace[0] 
        AceType:       0x02 (SYSTEM_AUDIT_ACE_TYPE) 
        AceFlags:      0xc0 
                           SUCCESSFUL_ACCESS_ACE_FLAG 
                           FAILED_ACCESS_ACE_FLAG 
        AceSize:       0x0014 
        Access Mask:   0x000d002b 
                            DELETE 
                            WRITE_DAC 
                            WRITE_OWNER 
                            Others(0x0000002b) 
        Ace Sid:       S-1-1-0 


 

アカウント系は以下のような出力となります。

> acehack -account "network service" 
Account: network service 
Domain:  NT AUTHORITY 
SID:     S-1-5-20 
Type:    SidTypeWellKnownGroup

>acehack -sid S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464 
SID:     S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464 
Type:    SidTypeWellKnownGroup 
Account: NT SERVICE\TrustedInstaller 


ソースファイルは以下の 2 つです。参考にした URL をところどころにコメントとして入れてあります。 ビルドする場合は、rpcrt4.lib もリンクさせる必要があります。これは GUID 構造体→ 文字列 の返還に UuidToString を使っているためです。

まずは main.cpp
引数をパースしているだけです。

// 
// main.cpp 
//

#include <Windows.h> 
#include <stdio.h> 
#include <ctype.h> 
#include <string.h>

#define MAXLEN_OPMODE 16

BOOL OpmodeSid(LPCWSTR StringSid); 
BOOL OpmodeAccount(LPCWSTR Account); 
// BOOL OpmodeAccount(); not used 
BOOL OpmodeSddl(LPCWSTR Sddl);

/*

#### Usage

  acehack opmode <Option>

#### Opmode

  SID ( http://msdn.microsoft.com/en-us/library/aa379597 )

    acehack -SID S-1-5-64-21 
    acehack -SID BA 
      shows user or group name from specified SID 

  SDDL ( http://msdn.microsoft.com/en-us/library/aa379567(v=VS.85).aspx )

    acehack -SDDL D:(A;ID;FA;;;BA)(A;ID;FA;;;SY)(A;ID;0x1301bf;;;AU)(A;ID;0x1200a9;;;BU) 
      parses specified SDDL string

  Account

    acehack -Account System 
      shows account SID

*/

void ShowUsage() { 
    wprintf(L"\n#### Usage\n\n  acehack opmode <Option>\n\n#### Opmode\n\n"); 
    wprintf(L"  SID  ( http://msdn.microsoft.com/en-us/library/aa379597 )\n\n"); 
    wprintf(L"    acehack -SID S-1-5-64-21\n"); 
    wprintf(L"    acehack -SID BA\n      shows user or group name from specified SID \n\n"); 
    wprintf(L"  SDDL ( http://msdn.microsoft.com/en-us/library/aa379567(v=VS.85).aspx )\n\n"); 
    wprintf(L"    acehack -SDDL D:(A;ID;FA;;;BA)(A;ID;FA;;;SY)(A;ID;0x1301bf;;;AU)(A;ID;0x1200a9;;;BU)\n"); 
    wprintf(L"      parses specified SDDL string\n\n  Account\n\n"); 
    wprintf(L"    acehack -Account System\n      shows account SID\n"); 
}

static wchar_t upperstr[MAXLEN_OPMODE+1]; 
const wchar_t *ToUpper(const wchar_t *s) { 
    for ( int i=0 ; i<MAXLEN_OPMODE+1 ; ++i ) { 
        upperstr[i]= toupper(s[i]); 
        if ( s[i]==0 ) 
            return upperstr; 
    } 
    upperstr[MAXLEN_OPMODE]= 0; 
    return upperstr; 
}

int wmain(int argc, wchar_t *argv[]) { 
    if ( argc<3 ) { 
        ShowUsage(); 
        return 1; 
    }

    const wchar_t *UpperOpmode= ToUpper(argv[1]); 
    if ( wcscmp(UpperOpmode, L"-SID")==0 ) { 
        OpmodeSid(argv[2]); 
    } 
    else if ( wcscmp(UpperOpmode, L"-SDDL")==0 ) { 
        OpmodeSddl(argv[2]); 
    } 
    else if ( wcscmp(UpperOpmode, L"-ACCOUNT")==0 ) { 
        OpmodeAccount(argv[2]); 
    } 
    else { 
        wprintf(L"%s  =>  bad command.\n", argv[1]); 
        return 1; 
    }

    return 0; 
}

次にメインルーチンの acehack.cpp
定数定義に行数を消費しまくりです。

// 
// acehack.cpp 
//

#include <Windows.h> 
#include <Sddl.h> 
#include <strsafe.h> 
#include <stdio.h> 
#include <Iads.h>

BOOL OpmodeAccount(); 
BOOL OpmodeAccount(LPCWSTR Account); 
BOOL OpmodeSid(LPCWSTR StringSid); 
BOOL OpmodeSddl(LPCWSTR Sddl);

/* 
http://msdn.microsoft.com/en-us/library/aa379601(VS.85).aspx

typedef enum _SID_NAME_USE { 
    SidTypeUser = 1, 
    SidTypeGroup, 
    SidTypeDomain, 
    SidTypeAlias, 
    SidTypeWellKnownGroup, 
    SidTypeDeletedAccount, 
    SidTypeInvalid, 
    SidTypeUnknown, 
    SidTypeComputer, 
    SidTypeLabel 
} SID_NAME_USE, *PSID_NAME_USE; 
*/ 
#define MAX_SIDTYPE 32 
const wchar_t SidTypeMapping[][MAX_SIDTYPE]= { 
    L"", 
    L"SidTypeUser", 
    L"SidTypeGroup", 
    L"SidTypeDomain", 
    L"SidTypeAlias", 
    L"SidTypeWellKnownGroup", 
    L"SidTypeDeletedAccount", 
    L"SidTypeInvalid", 
    L"SidTypeUnknown", 
    L"SidTypeComputer", 
    L"SidTypeLabel" 
};

/* 
http://msdn.microsoft.com/en-us/library/aa379566(v=VS.85).aspx

#define SE_OWNER_DEFAULTED               (0x0001) 
#define SE_GROUP_DEFAULTED               (0x0002) 
#define SE_DACL_PRESENT                  (0x0004) 
#define SE_DACL_DEFAULTED                (0x0008) 
#define SE_SACL_PRESENT                  (0x0010) 
#define SE_SACL_DEFAULTED                (0x0020) 
#define SE_DACL_AUTO_INHERIT_REQ         (0x0100) 
#define SE_SACL_AUTO_INHERIT_REQ         (0x0200) 
#define SE_DACL_AUTO_INHERITED           (0x0400) 
#define SE_SACL_AUTO_INHERITED           (0x0800) 
#define SE_DACL_PROTECTED                (0x1000) 
#define SE_SACL_PROTECTED                (0x2000) 
#define SE_RM_CONTROL_VALID              (0x4000) 
#define SE_SELF_RELATIVE                 (0x8000) 
*/

#define MAX_SDCONTROL 32

struct SDCONTROL_MAPPING { 
    SECURITY_DESCRIPTOR_CONTROL Flag; 
    WCHAR ControlName[MAX_SDCONTROL]; 
}; 
const SDCONTROL_MAPPING SdControlMapping[]= { 
    {SE_OWNER_DEFAULTED, L"SE_OWNER_DEFAULTED"}, 
    {SE_GROUP_DEFAULTED, L"SE_GROUP_DEFAULTED"}, 
    {SE_DACL_PRESENT, L"SE_DACL_PRESENT"}, 
    {SE_DACL_DEFAULTED, L"SE_DACL_DEFAULTED"}, 
    {SE_SACL_PRESENT, L"SE_SACL_PRESENT"}, 
    {SE_SACL_DEFAULTED, L"SE_SACL_DEFAULTED"}, 
    {SE_DACL_AUTO_INHERIT_REQ, L"SE_DACL_AUTO_INHERIT_REQ"}, 
    {SE_SACL_AUTO_INHERIT_REQ, L"SE_SACL_AUTO_INHERIT_REQ"}, 
    {SE_DACL_AUTO_INHERITED, L"SE_DACL_AUTO_INHERITED"}, 
    {SE_SACL_AUTO_INHERITED, L"SE_SACL_AUTO_INHERITED"}, 
    {SE_DACL_PROTECTED, L"SE_DACL_PROTECTED"}, 
    {SE_SACL_PROTECTED, L"SE_SACL_PROTECTED"}, 
    {SE_RM_CONTROL_VALID, L"SE_RM_CONTROL_VALID"}, 
    {SE_SELF_RELATIVE, L"SE_SELF_RELATIVE"}, 
    {0, L""}, 
};

/* 
http://msdn.microsoft.com/en-us/library/aa374919

#define ACCESS_MIN_MS_ACE_TYPE                  (0x0)- 
#define ACCESS_ALLOWED_ACE_TYPE                 (0x0) 
#define ACCESS_DENIED_ACE_TYPE                  (0x1) 
#define SYSTEM_AUDIT_ACE_TYPE                   (0x2) 
#define SYSTEM_ALARM_ACE_TYPE                   (0x3) 
#define ACCESS_MAX_MS_V2_ACE_TYPE               (0x3)- 
#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE        (0x4) 
#define ACCESS_MAX_MS_V3_ACE_TYPE               (0x4)- 
#define ACCESS_MIN_MS_OBJECT_ACE_TYPE           (0x5)- 
#define ACCESS_ALLOWED_OBJECT_ACE_TYPE          (0x5) 
#define ACCESS_DENIED_OBJECT_ACE_TYPE           (0x6) 
#define SYSTEM_AUDIT_OBJECT_ACE_TYPE            (0x7) 
#define SYSTEM_ALARM_OBJECT_ACE_TYPE            (0x8) 
#define ACCESS_MAX_MS_OBJECT_ACE_TYPE           (0x8)- 
#define ACCESS_MAX_MS_V4_ACE_TYPE               (0x8)- 
#define ACCESS_MAX_MS_ACE_TYPE                  (0x8)- 
#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE        (0x9) 
#define ACCESS_DENIED_CALLBACK_ACE_TYPE         (0xA) 
#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE (0xB) 
#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE  (0xC) 
#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE          (0xD) 
#define SYSTEM_ALARM_CALLBACK_ACE_TYPE          (0xE) 
#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE   (0xF) 
#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE   (0x10) 
#define SYSTEM_MANDATORY_LABEL_ACE_TYPE         (0x11) 
#define ACCESS_MAX_MS_V5_ACE_TYPE               (0x11)- 
*/

#define MAX_ACETYPE 41 
const WCHAR AceTypeMapping[][MAX_ACETYPE]= { 
    L"ACCESS_ALLOWED_ACE_TYPE", 
    L"ACCESS_DENIED_ACE_TYPE", 
    L"SYSTEM_AUDIT_ACE_TYPE", 
    L"SYSTEM_ALARM_ACE_TYPE", 
    L"ACCESS_ALLOWED_COMPOUND_ACE_TYPE", 
    L"ACCESS_ALLOWED_OBJECT_ACE_TYPE", 
    L"ACCESS_DENIED_OBJECT_ACE_TYPE", 
    L"SYSTEM_AUDIT_OBJECT_ACE_TYPE", 
    L"SYSTEM_ALARM_OBJECT_ACE_TYPE", 
    L"ACCESS_ALLOWED_CALLBACK_ACE_TYPE", 
    L"ACCESS_DENIED_CALLBACK_ACE_TYPE", 
    L"ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE", 
    L"ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE", 
    L"SYSTEM_AUDIT_CALLBACK_ACE_TYPE", 
    L"SYSTEM_ALARM_CALLBACK_ACE_TYPE", 
    L"SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE", 
    L"SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE", 
    L"SYSTEM_MANDATORY_LABEL_ACE_TYPE",

    L"Unknown Type", 
};

/* 
#define OBJECT_INHERIT_ACE               (0x1) 
#define CONTAINER_INHERIT_ACE            (0x2) 
#define NO_PROPAGATE_INHERIT_ACE         (0x4) 
#define INHERIT_ONLY_ACE                 (0x8) 
#define INHERITED_ACE                    (0x10) 
#define SUCCESSFUL_ACCESS_ACE_FLAG       (0x40) 
#define FAILED_ACCESS_ACE_FLAG           (0x80) 
*/

#define MAX_ACEFLAG 32

struct ACEFLAG_MAPPING { 
    BYTE Flag; 
    WCHAR Name[MAX_ACEFLAG]; 
}; 
const ACEFLAG_MAPPING AceFlagMapping[]= { 
    {OBJECT_INHERIT_ACE, L"OBJECT_INHERIT_ACE"}, 
    {CONTAINER_INHERIT_ACE, L"CONTAINER_INHERIT_ACE"}, 
    {NO_PROPAGATE_INHERIT_ACE, L"NO_PROPAGATE_INHERIT_ACE"}, 
    {INHERIT_ONLY_ACE, L"INHERIT_ONLY_ACE"}, 
    {INHERITED_ACE, L"INHERITED_ACE"}, 
    {SUCCESSFUL_ACCESS_ACE_FLAG, L"SUCCESSFUL_ACCESS_ACE_FLAG"}, 
    {FAILED_ACCESS_ACE_FLAG, L"FAILED_ACCESS_ACE_FLAG"}, 
    {0, L""} 
};

/* 
http://msdn.microsoft.com/en-us/library/aa374892

#define DELETE                           (0x00010000L) 
#define READ_CONTROL                     (0x00020000L) 
#define WRITE_DAC                        (0x00040000L) 
#define WRITE_OWNER                      (0x00080000L) 
#define SYNCHRONIZE                      (0x00100000L) 
#define ACCESS_SYSTEM_SECURITY           (0x01000000L) 
#define MAXIMUM_ALLOWED                  (0x02000000L) 
#define GENERIC_ALL                      (0x10000000L) 
#define GENERIC_EXECUTE                  (0x20000000L) 
#define GENERIC_WRITE                    (0x40000000L) 
#define GENERIC_READ                     (0x80000000L) 
*/

#define MAX_ACESSMASK 40

struct ACCESSMASK_MAPPING { 
    ACCESS_MASK Flag; 
    WCHAR Name[MAX_ACESSMASK]; 
};

ACCESSMASK_MAPPING AccessMaskMaping[]= { 
    {DELETE, L"DELETE"}, 
    {READ_CONTROL, L"READ_CONTROL"}, 
    {WRITE_DAC, L"WRITE_DAC"}, 
    {WRITE_OWNER, L"WRITE_OWNER"}, 
    {SYNCHRONIZE, L"SYNCHRONIZE"}, 
    {ACCESS_SYSTEM_SECURITY, L"ACCESS_SYSTEM_SECURITY"}, 
    {MAXIMUM_ALLOWED, L"MAXIMUM_ALLOWED"}, 
    {GENERIC_ALL, L"GENERIC_ALL"}, 
    {GENERIC_EXECUTE, L"GENERIC_EXECUTE"}, 
    {GENERIC_WRITE, L"GENERIC_WRITE"}, 
    {GENERIC_READ, L"GENERIC_READ"}, 
    {0, L""} 
};

/* 
http://msdn.microsoft.com/en-us/library/aa965848

#define SYSTEM_MANDATORY_LABEL_NO_WRITE_UP         0x1 
#define SYSTEM_MANDATORY_LABEL_NO_READ_UP          0x2 
#define SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP       0x4 
*/ 
ACCESSMASK_MAPPING SysMandatoryMapping[]= { 
    {SYSTEM_MANDATORY_LABEL_NO_WRITE_UP, L"SYSTEM_MANDATORY_LABEL_NO_WRITE_UP"}, 
    {SYSTEM_MANDATORY_LABEL_NO_READ_UP, L"SYSTEM_MANDATORY_LABEL_NO_READ_UP"}, 
    {SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP, L"SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP"}, 
    {0, L""} 
};

/* 
http://msdn.microsoft.com/en-us/library/aa772285(v=vs.85).aspx

  ADS_RIGHT_DS_CREATE_CHILD          = 0x1, 
  ADS_RIGHT_DS_DELETE_CHILD          = 0x2, 
  ADS_RIGHT_ACTRL_DS_LIST            = 0x4, 
  ADS_RIGHT_DS_SELF                  = 0x8, 
  ADS_RIGHT_DS_READ_PROP             = 0x10, 
  ADS_RIGHT_DS_WRITE_PROP            = 0x20, 
  ADS_RIGHT_DS_DELETE_TREE           = 0x40, 
  ADS_RIGHT_DS_LIST_OBJECT           = 0x80, 
  ADS_RIGHT_DS_CONTROL_ACCESS        = 0x100 
*/

ACCESSMASK_MAPPING AdsRightMapping[]= { 
    {ADS_RIGHT_DS_CREATE_CHILD, L"ADS_RIGHT_DS_CREATE_CHILD"}, 
    {ADS_RIGHT_DS_DELETE_CHILD, L"ADS_RIGHT_DS_DELETE_CHILD"}, 
    {ADS_RIGHT_ACTRL_DS_LIST, L"ADS_RIGHT_ACTRL_DS_LIST"}, 
    {ADS_RIGHT_DS_SELF, L"ADS_RIGHT_DS_SELF"}, 
    {ADS_RIGHT_DS_READ_PROP, L"ADS_RIGHT_DS_READ_PROP"}, 
    {ADS_RIGHT_DS_WRITE_PROP, L"ADS_RIGHT_DS_WRITE_PROP"}, 
    {ADS_RIGHT_DS_LIST_OBJECT, L"ADS_RIGHT_DS_LIST_OBJECT"}, 
    {ADS_RIGHT_DS_CONTROL_ACCESS, L"ADS_RIGHT_DS_CONTROL_ACCESS"}, 
    {0, L""} 
};


/* 
http://msdn.microsoft.com/en-us/library/aa374857.aspx 
#define ACE_OBJECT_TYPE_PRESENT           0x1 
#define ACE_INHERITED_OBJECT_TYPE_PRESENT 0x2 
*/

#define MAX_OBJECTTYPE 64 
#define OBJECTTYPE_SUPPORTED_MAX ACE_OBJECT_TYPE_PRESENT|ACE_INHERITED_OBJECT_TYPE_PRESENT

const WCHAR ObjectTypeMapping[][MAX_OBJECTTYPE]= { 
    L"", 
    L"ACE_OBJECT_TYPE_PRESENT", 
    L"ACE_INHERITED_OBJECT_TYPE_PRESENT", 
    L"ACE_OBJECT_TYPE_PRESENT|ACE_INHERITED_OBJECT_TYPE_PRESENT", 
    
    L"Unknown" 
};


#define GOTO_CLEANUP(ERRMSG) \ 
    if ( !ret ) { \ 
        wprintf(ERRMSG, GetLastError()); \ 
        ret= FALSE; \ 
        goto cleanup; \ 
    }

BOOL OpmodeSid(LPCWSTR StringSid) { 
    BOOL ret= FALSE; 
    PSID Sid= NULL; 
    DWORD NameLength= 0; 
    DWORD DomainLength= 0; 
    SID_NAME_USE SidType; 
    LPWSTR Account= NULL; 
    LPWSTR Name= NULL;

    ret= ConvertStringSidToSid(StringSid, &Sid); 
    GOTO_CLEANUP(L"ConvertStringSidToSid failed (%d)\n");

    ret= LookupAccountSid(NULL, Sid, 
        NULL, &NameLength, 
        NULL, &DomainLength, 
        &SidType); 
    
    int LenTotal= DomainLength+NameLength+1; 
    Account= new WCHAR[NameLength]; 
    Name= new WCHAR[LenTotal]; 
    ret= LookupAccountSid(NULL, Sid, 
        Account, &NameLength, 
        Name, &DomainLength, 
        &SidType); 
    GOTO_CLEANUP(L"LookupAccountSid failed (%d)\n"); 
    
    StringCchCat(Name, LenTotal, L"\\"); 
    StringCchCat(Name, LenTotal, Account);

    // result 
    wprintf(L"SID:     %s\n", StringSid); 
    wprintf(L"Type:    %s\n", SidTypeMapping[SidType]); 
    wprintf(L"Account: %s\n", Name);

    ret= TRUE;

cleanup: 
    if ( Account ) delete [] Account; 
    if ( Name ) delete [] Name; 
    if ( Sid ) LocalFree(Sid);

    return ret; 
}

BOOL OpmodeAccount() { 
    BOOL ret= FALSE; 
    LPWSTR User= NULL; 
    DWORD UserLength= 0;

    ret= GetUserName(NULL, &UserLength); 
    if ( !ret && GetLastError()!=ERROR_INSUFFICIENT_BUFFER ) { 
        wprintf(L"GetUserName failed (%d)\n", GetLastError()); 
        ret= FALSE; 
        goto cleanup; 
    }

    User= new WCHAR[UserLength]; 
    ret= GetUserName(User, &UserLength); 
    GOTO_CLEANUP(L"GetUserName failed (%d)\n");

    OpmodeAccount(User);

    return TRUE;

cleanup: 
    if ( User ) delete [] User; 
    return ret; 
}

BOOL OpmodeAccount(LPCWSTR Account) { 
    BOOL ret= FALSE; 
    PSID Sid= NULL; 
    DWORD SidLength= 0; 
    LPWSTR Domain= NULL; 
    DWORD DomainLength= 0; 
    SID_NAME_USE SidType; 
    LPWSTR StringSid= NULL;

    ret= LookupAccountName(NULL, Account, 
        NULL, &SidLength, 
        NULL, &DomainLength, 
        &SidType);

    Sid= new BYTE[SidLength]; 
    Domain= new WCHAR[DomainLength];

    ret= LookupAccountName(NULL, Account, 
        Sid, &SidLength, 
        Domain, &DomainLength, 
        &SidType); 
    GOTO_CLEANUP(L"LookupAccountName failed (%d)\n");

    ret= ConvertSidToStringSid(Sid, &StringSid); 
    GOTO_CLEANUP(L"ConvertSidToStringSid failed (%d)\n");

    // result 
    wprintf(L"Account: %s\n", Account); 
    wprintf(L"Domain:  %s\n", Domain); 
    wprintf(L"SID:     %s\n", StringSid); 
    wprintf(L"Type:    %s\n", SidTypeMapping[SidType]);

    ret= TRUE;

cleanup: 
    if ( StringSid ) LocalFree(StringSid); 
    if ( Sid ) delete [] Sid; 
    if ( Domain ) delete [] Domain; 
    return ret; 
}

// http://msdn.microsoft.com/en-us/library/aa374912(v=VS.85).aspx 
// http://msdn.microsoft.com/en-us/library/aa374919 
void ShowAce(BYTE AceType, PVOID RawAce) { 
    BOOL ret= FALSE; 
    int i= 0;

    switch ( AceType ) { 
    case ACCESS_ALLOWED_ACE_TYPE: 
    case ACCESS_ALLOWED_CALLBACK_ACE_TYPE: 
    case ACCESS_DENIED_ACE_TYPE: 
    case ACCESS_DENIED_CALLBACK_ACE_TYPE: 
    case SYSTEM_ALARM_ACE_TYPE: 
    case SYSTEM_ALARM_CALLBACK_ACE_TYPE: 
    case SYSTEM_AUDIT_ACE_TYPE: 
    case SYSTEM_AUDIT_CALLBACK_ACE_TYPE: 
    case SYSTEM_MANDATORY_LABEL_ACE_TYPE: 
        { 
            PACCESS_ALLOWED_ACE Ace= (PACCESS_ALLOWED_ACE)RawAce; 
            LPWSTR Sid= NULL; LPCWSTR SidUnknown= L"Unknown"; 
            ret= ConvertSidToStringSid((PSID)&Ace->SidStart, &Sid); 
            if ( !ret ) 
                wprintf(L"ConvertSidToStringSid failed (%d)\n", 
                  GetLastError());

            ACCESS_MASK Mask= Ace->Mask; 
            wprintf(L"        Access Mask:   0x%08x\n", Mask); 
            for ( i=0 ; AccessMaskMaping[i].Flag!=0 ; ++i ) { 
                if ( AccessMaskMaping[i].Flag&Mask ) { 
                    wprintf(L"                            %s\n", 
                      AccessMaskMaping[i].Name); 
                    Mask&= ~AccessMaskMaping[i].Flag; 
                } 
            } 
            if ( Mask ) { 
                if ( AceType==SYSTEM_MANDATORY_LABEL_ACE_TYPE ) { 
                    for ( i=0 ; SysMandatoryMapping[i].Flag!=0 ; ++i ) { 
                        if ( SysMandatoryMapping[i].Flag&Mask ) { 
                            wprintf(L"                            %s\n", 
                              SysMandatoryMapping[i].Name); 
                            Mask&= ~SysMandatoryMapping[i].Flag; 
                        } 
                    } 
                }

                if ( Mask ) 
                    wprintf(L"                            Others(0x%08x)\n", 
                      Mask); 
            }

            wprintf(L"        Ace Sid:       %s\n", Sid ? Sid : SidUnknown);

            if ( Sid) LocalFree(Sid); 
        } 
        break; 
        
    case ACCESS_ALLOWED_OBJECT_ACE_TYPE: 
    case ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE: 
    case ACCESS_DENIED_OBJECT_ACE_TYPE: 
    case ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE: 
    case SYSTEM_ALARM_OBJECT_ACE_TYPE: 
    case SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE: 
    case SYSTEM_AUDIT_OBJECT_ACE_TYPE: 
    case SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE: 
        { 
            PACCESS_ALLOWED_OBJECT_ACE Ace= 
              (PACCESS_ALLOWED_OBJECT_ACE)RawAce; 
            LPWSTR Sid= NULL; LPCWSTR SidUnknown= L"Unknown"; 
            LPGUID GuidObj= NULL; 
            LPGUID GuidInheritedObj= NULL; 
            PSID SidOffset= NULL; 
            RPC_WSTR GuidString= NULL; 
            RPC_STATUS RpcRet= RPC_S_OK;

            ACCESS_MASK Mask= Ace->Mask; 
            wprintf(L"        Access Mask:   0x%08x\n", Mask); 
            for ( i=0 ; AccessMaskMaping[i].Flag!=0 ; ++i ) { 
                if ( AccessMaskMaping[i].Flag&Mask ) { 
                    wprintf(L"                            %s\n", 
                      AccessMaskMaping[i].Name); 
                    Mask&= ~AccessMaskMaping[i].Flag; 
                } 
            } 
            if ( Mask ) { 
                if ( Ace->Flags&ACE_OBJECT_TYPE_PRESENT ) { 
                    for ( i=0 ; AdsRightMapping[i].Flag!=0 ; ++i ) { 
                        if ( AdsRightMapping[i].Flag&Mask ) { 
                            wprintf(L"                            %s\n", 
                              AdsRightMapping[i].Name); 
                            Mask&= ~AdsRightMapping[i].Flag; 
                        } 
                    } 
                }

                if ( Mask ) 
                    wprintf(L"                            Others(0x%08x)\n", 
                      Mask); 
            } 
            
            wprintf(L"        Access Flags:  0x%08x (%s)\n", Ace->Flags, 
                ObjectTypeMapping[min( 
                  Ace->Flags, OBJECTTYPE_SUPPORTED_MAX+1)]); 
            switch ( Ace->Flags ) { 
            case 0: 
                GuidObj= GuidInheritedObj= NULL; 
                SidOffset= (PSID)&Ace->ObjectType; 
                break; 
            case ACE_OBJECT_TYPE_PRESENT: 
                GuidObj= &Ace->ObjectType; 
                GuidInheritedObj= NULL; 
                SidOffset= (PSID)&Ace->InheritedObjectType; 
                break; 
            case ACE_INHERITED_OBJECT_TYPE_PRESENT: 
                GuidObj= NULL; 
                GuidInheritedObj= &Ace->ObjectType; 
                SidOffset= (PSID)&Ace->InheritedObjectType; 
                break; 
            case ACE_OBJECT_TYPE_PRESENT|ACE_INHERITED_OBJECT_TYPE_PRESENT: 
                GuidObj= &Ace->ObjectType; 
                GuidInheritedObj= &Ace->InheritedObjectType; 
                SidOffset= (PSID)&Ace->SidStart; 
                break; 
            default: 
                GuidObj= GuidInheritedObj= NULL; 
                SidOffset= NULL; 
                break; 
            } 
            
            if ( GuidObj ) { 
                RpcRet= UuidToString(GuidObj, &GuidString); 
                if ( RpcRet==RPC_S_OK ) 
                    wprintf(L"        ObjectType:    {%s}i\n", GuidString); 
                else 
                    wprintf(L"UuidToString failed (%d)\n", RpcRet);

                if ( GuidString ) RpcStringFree(&GuidString); 
            } 
            else 
                wprintf(L"        ObjectType:    Not defined\n");

            if ( GuidInheritedObj ) { 
                RpcRet= UuidToString(GuidObj, &GuidString); 
                
                if ( RpcRet==RPC_S_OK ) 
                    wprintf(L"        InhObjectType: {%s}\n", GuidString); 
                else 
                    wprintf(L"UuidToString failed (%d)\n", RpcRet);

                if ( GuidString ) RpcStringFree(&GuidString); 
            } 
            else 
                wprintf(L"        InhObjectType: Not defined\n"); 
            
            ret= ConvertSidToStringSid(SidOffset, &Sid); 
            if ( !ret ) 
                wprintf(L"ConvertSidToStringSid failed (%d)\n", 
                  GetLastError()); 
            wprintf(L"        Ace Sid:       %s\n", Sid ? Sid : SidUnknown);

            if ( Sid) LocalFree(Sid); 
        } 
        break; 
         
        
    case ACCESS_ALLOWED_COMPOUND_ACE_TYPE: // reserved 
    default: 
        break; 
    } 
}

// http://msdn.microsoft.com/en-us/library/aa374912(v=VS.85).aspx 
void ShowAcl(PACL Acl) { 
    if ( Acl==NULL || Acl->AceCount==0 ) 
        return;

    BOOL ret= FALSE; 
    PACE_HEADER AceHeader= NULL;

    for ( int i=0 ; i<Acl->AceCount ; ++i ) { 
        AceHeader= NULL; 
        ret= GetAce(Acl, i, (LPVOID*)&AceHeader); 
        if ( !ret ) { 
            wprintf(L"    Ace[%d] -> GetAce failed (%d)\n", 
              i, GetLastError()); 
            continue; 
        }

        wprintf(L"    Ace[%d]\n", i); 
        wprintf(L"        AceType:       0x%02x (%s)\n", AceHeader->AceType, 
            AceTypeMapping[min(AceHeader->AceType, 
              ACCESS_MAX_MS_V5_ACE_TYPE+1)]); 
        wprintf(L"        AceFlags:      0x%02x\n", AceHeader->AceFlags); 
        for ( int i=0 ; AceFlagMapping[i].Flag!=0 ; ++i ) { 
            if ( AceHeader->AceFlags&AceFlagMapping[i].Flag ) 
                wprintf(L"                           %s\n", 
                  AceFlagMapping[i].Name); 
        } 
        wprintf(L"        AceSize:       0x%04x\n", AceHeader->AceSize);

        ShowAce(AceHeader->AceType, AceHeader); 
    } 
}

BOOL OpmodeSddl(LPCWSTR Sddl) { 
    BOOL ret= FALSE; 
    PSECURITY_DESCRIPTOR Sd= NULL; 
    ULONG SdLength= 0; 
    SECURITY_DESCRIPTOR_CONTROL SdCtrl= 0; 
    DWORD SdRevision= 0; 
    UCHAR RmControl= 0; 
    BOOL Default= FALSE; 
    PSID Owner= NULL; 
    LPWSTR OwnerString= NULL; 
    PSID Group= NULL; 
    LPWSTR GroupString= NULL; 
    BOOL DaclPresent= FALSE; 
    PACL Dacl= NULL; 
    BOOL SaclPresent= FALSE; 
    PACL Sacl= NULL;

    ret= ConvertStringSecurityDescriptorToSecurityDescriptor( 
        Sddl, SDDL_REVISION_1, &Sd, &SdLength); 
    GOTO_CLEANUP( 
      L"ConvertStringSecurityDescriptorToSecurityDescriptor failed (%d)\n");

    // Control & Revision 
    ret= GetSecurityDescriptorControl(Sd, &SdCtrl, &SdRevision); 
    GOTO_CLEANUP(L"GetSecurityDescriptorControl failed (%d)\n");

    // RMControl 
    ret= GetSecurityDescriptorRMControl(Sd, &RmControl); 
    GOTO_CLEANUP(L"GetSecurityDescriptorRMControl failed (%d)\n");

    // Owner 
    ret= GetSecurityDescriptorOwner(Sd, &Owner, &Default); 
    GOTO_CLEANUP(L"GetSecurityDescriptorOwner failed (%d)\n"); 
    if ( Owner ) { 
        ret= ConvertSidToStringSid(Owner, &OwnerString); 
        GOTO_CLEANUP(L"ConvertSidToStringSid failed (%d)\n"); 
    }

    // PrimaryGroup 
    ret= GetSecurityDescriptorGroup(Sd, &Group, &Default); 
    GOTO_CLEANUP(L"GetSecurityDescriptorGroup failed (%d)\n"); 
    if ( Group ) { 
        ret= ConvertSidToStringSid(Group, &GroupString); 
        GOTO_CLEANUP(L"ConvertSidToStringSid failed (%d)\n"); 
    }

    // Dacl 
    ret= GetSecurityDescriptorDacl(Sd, &DaclPresent, &Dacl, &Default); 
    GOTO_CLEANUP(L"GetSecurityDescriptorDacl failed (%d)\n");

    // Sacl 
    ret= GetSecurityDescriptorSacl(Sd, &SaclPresent, &Sacl, &Default); 
    GOTO_CLEANUP(L"GetSecurityDescriptorSacl failed (%d)\n");

    // result 
    wprintf(L"%s\n", Sddl); 
    wprintf(L"Revision:     0x%08x\n", SdRevision);

    SECURITY_DESCRIPTOR_CONTROL SdCtrlCopy= SdCtrl; 
    wprintf(L"Control:      0x%04x\n", SdCtrl); 
    for ( int i=0 ; SdControlMapping[i].Flag!=0 ; ++i ) { 
        if ( SdControlMapping[i].Flag&SdCtrl ) { 
            wprintf(L"                  %s\n", 
              SdControlMapping[i].ControlName); 
            SdCtrlCopy&= ~SdControlMapping[i].Flag; 
        } 
    } 
    if ( SdCtrlCopy ) 
        wprintf(L"                  Others(0x%04x)\n", SdCtrlCopy);

    wprintf(L"RMControl:    0x%02x\n", RmControl); 
    
    if ( Owner ) 
        wprintf(L"Owner:        %s\n", OwnerString); 
    else 
        wprintf(L"Owner:        No owner\n");

    if ( Group ) 
        wprintf(L"PrimaryGroup: %s\n", GroupString); 
    else 
        wprintf(L"PrimaryGroup: No group\n");

    wprintf(L"\n");

    if ( !DaclPresent ) 
        wprintf(L"No DACL\n"); 
    else if ( Dacl==NULL ) 
        // NULL ACLs are not supported in SDDL, but just in case... 
        wprintf(L"NULL DACL\n"); 
    else { 
        wprintf(L"DACL\n"); 
        wprintf(L"    Revision: 0x%02x\n", Dacl->AclRevision); 
        wprintf(L"    Size:     0x%04x\n", Dacl->AclSize); 
        wprintf(L"    AceCount: 0x%04x\n", Dacl->AceCount); 
        ShowAcl(Dacl); 
    } 
    
    wprintf(L"\n"); 
    
    if ( !SaclPresent ) 
        wprintf(L"No SACL\n"); 
    else if ( Sacl==NULL ) 
        wprintf(L"NULL SACL\n"); 
    else { 
        wprintf(L"SACL\n"); 
        wprintf(L"    Revision: 0x%02x\n", Sacl->AclRevision); 
        wprintf(L"    Size:     0x%04x\n", Sacl->AclSize); 
        wprintf(L"    AceCount: 0x%04x\n", Sacl->AceCount); 
        ShowAcl(Sacl); 
    }

    ret= TRUE; 
    
cleanup: 
    if ( OwnerString ) LocalFree(OwnerString); 
    if ( Sd ) LocalFree(Sd); 
    return ret; 
}