//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1996 - 1999
//
//  File:       misc.cpp
//
//  This file contains miscellaneous helper functions.
//
//--------------------------------------------------------------------------

#include "aclpriv.h"

/*******************************************************************

    NAME:       GetAceSid

    SYNOPSIS:   Gets pointer to SID from an ACE

    ENTRY:      pAce - pointer to ACE

    EXIT:

    RETURNS:    Pointer to SID if successful, NULL otherwise

    NOTES:

    HISTORY:
        JeffreyS    08-Oct-1996     Created

********************************************************************/

PSID
GetAceSid(PACE_HEADER pAce)
{
    switch (pAce->AceType)
    {
    case ACCESS_ALLOWED_ACE_TYPE:
    case ACCESS_DENIED_ACE_TYPE:
    case SYSTEM_AUDIT_ACE_TYPE:
    case SYSTEM_ALARM_ACE_TYPE:
        return (PSID)&((PKNOWN_ACE)pAce)->SidStart;

    case ACCESS_ALLOWED_COMPOUND_ACE_TYPE:
        return (PSID)&((PKNOWN_COMPOUND_ACE)pAce)->SidStart;

    case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
    case ACCESS_DENIED_OBJECT_ACE_TYPE:
    case SYSTEM_AUDIT_OBJECT_ACE_TYPE:
    case SYSTEM_ALARM_OBJECT_ACE_TYPE:
        return RtlObjectAceSid(pAce);
    }

    return NULL;
}


/*******************************************************************

    NAME:       LocalAllocSid

    SYNOPSIS:   Copies a SID

    ENTRY:      pOriginal - pointer to SID to copy

    EXIT:

    RETURNS:    Pointer to SID if successful, NULL otherwise

    NOTES:      Caller must free the returned SID with LocalFree

    HISTORY:
        JeffreyS    12-Apr-1999     Created

********************************************************************/

PSID
LocalAllocSid(PSID pOriginal)
{
    PSID pCopy = NULL;
    if (pOriginal && IsValidSid(pOriginal))
    {
        DWORD dwLength = GetLengthSid(pOriginal);
        pCopy = (PSID)LocalAlloc(LMEM_FIXED, dwLength);
        if (NULL != pCopy)
            CopyMemory(pCopy, pOriginal, dwLength);
    }
    return pCopy;
}


/*******************************************************************

    NAME:       DestroyDPA

    SYNOPSIS:   LocalFree's all pointers in a Dynamic Pointer
                Array and then frees the DPA.

    ENTRY:      hList - handle of list to destroy

    EXIT:

    RETURNS:    nothing

    NOTES:

    HISTORY:
        JeffreyS    08-Oct-1996     Created

********************************************************************/

int CALLBACK
_LocalFreeCB(LPVOID pVoid, LPVOID /*pData*/)
{
    if (pVoid)
        LocalFree(pVoid);
    return 1;
}

void
DestroyDPA(HDPA hList)
{
    if (hList != NULL)
        DPA_DestroyCallback(hList, _LocalFreeCB, 0);
}



/*******************************************************************

    NAME:       GetLSAConnection

    SYNOPSIS:   Wrapper for LsaOpenPolicy

    ENTRY:      pszServer - the server on which to make the connection

    EXIT:

    RETURNS:    LSA_HANDLE if successful, NULL otherwise

    NOTES:

    HISTORY:
        JeffreyS    08-Oct-1996     Created

********************************************************************/

LSA_HANDLE
GetLSAConnection(LPCTSTR pszServer, DWORD dwAccessDesired)
{
    LSA_HANDLE hPolicy = NULL;
    LSA_UNICODE_STRING uszServer = {0};
    LSA_UNICODE_STRING *puszServer = NULL;
    LSA_OBJECT_ATTRIBUTES oa;
    SECURITY_QUALITY_OF_SERVICE sqos;

    sqos.Length = SIZEOF(sqos);
    sqos.ImpersonationLevel = SecurityImpersonation;
    sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
    sqos.EffectiveOnly = FALSE;

    InitializeObjectAttributes(&oa, NULL, 0, NULL, NULL);
    oa.SecurityQualityOfService = &sqos;

    if (pszServer &&
        *pszServer &&
        RtlCreateUnicodeString(&uszServer, pszServer))
    {
        puszServer = &uszServer;
    }

    LsaOpenPolicy(puszServer, &oa, dwAccessDesired, &hPolicy);

    if (puszServer)
        RtlFreeUnicodeString(puszServer);

    return hPolicy;
}


/*******************************************************************

    NAME:       LookupSid

    SYNOPSIS:   Gets the qualified account name for a given SID

    ENTRY:      pszServer - the server on which to do the lookup
                pSid - the SID to lookup

    EXIT:       *ppszName contains the account name. This buffer
                must be freed by the caller with LocalFree.

                *pSidType contains the SID type. pSidType is optional.

    RETURNS:    TRUE if successful, FALSE otherwise

    NOTES:

    HISTORY:
        JeffreyS    08-Oct-1996     Created
        JeffreyS    16-Jan-1998     Converted to HDPA (multiple lookup)

********************************************************************/

BOOL
LookupSids(HDPA hSids, LPCTSTR pszServer, LPSECURITYINFO2 psi2, PUSER_LIST *ppUserList)
{
    PSIDCACHE pSidCache;

    if (NULL == hSids)
        return FALSE;

    if (ppUserList != NULL)
        *ppUserList = NULL;

    pSidCache = GetSidCache();

    if (NULL != pSidCache)
    {
        BOOL bRet = pSidCache->LookupSids(hSids, pszServer, psi2, ppUserList);
        pSidCache->Release();
        return bRet;
    }

    return FALSE;
}

BOOL
LookupSid(PSID pSid, LPCTSTR pszServer, LPSECURITYINFO2 psi2, PUSER_LIST *ppUserList)
{
    BOOL fResult;
    HDPA hSids = NULL;

    if (NULL == pSid)
        return FALSE;

    hSids = DPA_Create(1);

    if (NULL == hSids)
        return FALSE;

    DPA_AppendPtr(hSids, pSid);

    fResult = LookupSids(hSids, pszServer, psi2, ppUserList);

    if (NULL != hSids)
        DPA_Destroy(hSids);

    return fResult;
}

// Private data structure used by LookupSidsAsync to pass
// data needed by the thread
typedef struct _LOOKUPSIDSDATA
{
    HDPA hSids;
    LPTSTR pszServer;
    HWND hWndNotify;
    UINT uMsgNotify;
} LOOKUPSIDSDATA, *PLOOKUPSIDSDATA;


DWORD WINAPI
_LookupSidsAsyncProc(LPVOID pv)
{
    PLOOKUPSIDSDATA pdata = (PLOOKUPSIDSDATA)pv;

    if (pdata)
    {
        PSIDCACHE pSidCache = GetSidCache();

        if (NULL != pSidCache)
        {
            pSidCache->LookupSidsAsync(pdata->hSids,
                                       pdata->pszServer,
                                       NULL,
                                       pdata->hWndNotify,
                                       pdata->uMsgNotify);
            pSidCache->Release();
        }

        PostMessage(pdata->hWndNotify, pdata->uMsgNotify, 0, 0);

        DestroyDPA(pdata->hSids);
        LocalFreeString(&pdata->pszServer);
        LocalFree(pdata);
    }

    FreeLibraryAndExitThread(GetModuleHandle(c_szDllName), 0);
    return 0;
}

BOOL
LookupSidsAsync(HDPA hSids,
                LPCTSTR pszServer,
                LPSECURITYINFO2 psi2,
                HWND hWndNotify,
                UINT uMsgNotify,
                PHANDLE phThread)
{
    PLOOKUPSIDSDATA pdata;

    if (phThread)
        *phThread = NULL;

    if (NULL == hSids)
        return FALSE;

    if (psi2)
    {
        // Should marshal psi2 into a stream and do this on the
        // other thread.  Well No one has implemented psi2 so its fine.
        BOOL bResult = LookupSids(hSids, pszServer, psi2, NULL);
        PostMessage(hWndNotify, uMsgNotify, 0, 0);
        return bResult;
    }

    //
    // Copy all of the data so the thread can be abandoned if necessary
    //
    pdata = (PLOOKUPSIDSDATA)LocalAlloc(LPTR, SIZEOF(LOOKUPSIDSDATA));
    if (pdata)
    {
        int cSids;
        int i;
        HINSTANCE hInstThisDll;
        DWORD dwThreadId;
        HANDLE hThread;

        cSids = DPA_GetPtrCount(hSids);
        pdata->hSids = DPA_Create(cSids);

        if (!pdata->hSids)
        {
            LocalFree(pdata);
            return FALSE;
        }

        for (i = 0; i < cSids; i++)
        {
            PSID p2 = LocalAllocSid((PSID)DPA_FastGetPtr(hSids, i));
            if (p2)
            {
                DPA_AppendPtr(pdata->hSids, p2);
            }
        }

        if (pszServer)
            LocalAllocString(&pdata->pszServer, pszServer);

        pdata->hWndNotify = hWndNotify;
        pdata->uMsgNotify = uMsgNotify;

        // Give the thread we are about to create a ref to the dll,
        // so that the dll will remain for the lifetime of the thread
        hInstThisDll = LoadLibrary(c_szDllName);

        hThread = CreateThread(NULL,
                               0,
                               _LookupSidsAsyncProc,
                               pdata,
                               NULL,
                               &dwThreadId);
        if (hThread != NULL)
        {
            if (phThread)
                *phThread = hThread;
            else
                CloseHandle(hThread);
            return TRUE;
        }
        else
        {
            // Thread creation has failed; clean up
            DestroyDPA(pdata->hSids);
            LocalFreeString(&pdata->pszServer);
            LocalFree(pdata);
            FreeLibrary(hInstThisDll);
        }
    }
    return FALSE;
}

BOOL
BuildUserDisplayName(LPTSTR *ppszDisplayName,
                     LPCTSTR pszName,
                     LPCTSTR pszLogonName)
{
    TCHAR szDisplayName[MAX_PATH];

    if (NULL == ppszDisplayName || NULL == pszName)
        return FALSE;

    *ppszDisplayName = NULL;

    if (NULL != pszLogonName && *pszLogonName)
    {
        return (BOOL)FormatStringID(ppszDisplayName,
                                    ::hModule,
                                    IDS_FMT_USER_DISPLAY,
                                    pszName,
                                    pszLogonName);
    }

    return SUCCEEDED(LocalAllocString(ppszDisplayName, pszName));
}


/*******************************************************************

    NAME:       LoadImageList

    SYNOPSIS:   Creates an image list from a bitmap resource

    ENTRY:      hInstance - the bitmap lives here
                pszBitmapID - resource ID of the bitmap

    EXIT:

    RETURNS:    HIMAGELIST if successful, NULL otherwise

    NOTES:
        In order to calculate the number of images, it is assumed
        that the width and height of a single image are the same.

    HISTORY:
        JeffreyS    08-Oct-1996     Created

********************************************************************/

HIMAGELIST
LoadImageList(HINSTANCE hInstance, LPCTSTR pszBitmapID)
{
    HIMAGELIST himl = NULL;
    HBITMAP hbm = LoadBitmap(hInstance, pszBitmapID);

    if (hbm != NULL)
    {
        BITMAP bm;
        GetObject(hbm, SIZEOF(bm), &bm);

        himl = ImageList_Create(bm.bmHeight,    // height == width
                                bm.bmHeight,
                                ILC_COLOR | ILC_MASK,
                                bm.bmWidth / bm.bmHeight,
                                0);  // don't need to grow
        if (himl != NULL)
            ImageList_AddMasked(himl, hbm, CLR_DEFAULT);

        DeleteObject(hbm);
    }

    return himl;
}


/*******************************************************************

    NAME:       GetSidImageIndex

    SYNOPSIS:   Gets the image index for the given SID type

    ENTRY:      sidType - type of SID
                sidSys - well-known group type
                fRemoteUser - TRUE if SID is a user on a remote system

    EXIT:

    RETURNS:    index into image list

    NOTES:

    HISTORY:
        JeffreyS    08-Oct-1996     Created

********************************************************************/

SID_IMAGE_INDEX
GetSidImageIndex(PSID psid,
                 SID_NAME_USE sidType)
{
    SID_IMAGE_INDEX idBitmap;

    switch (sidType)
    {
    case SidTypeUser:
        idBitmap = SID_IMAGE_USER;
        break;

    case SidTypeAlias:
    case SidTypeGroup:
    case SidTypeWellKnownGroup:
        idBitmap = SID_IMAGE_GROUP;
        break;

#if(_WIN32_WINNT >= 0x0500)
    case SidTypeComputer:
        idBitmap = SID_IMAGE_COMPUTER;
        break;
#endif

    default:
        idBitmap = SID_IMAGE_UNKNOWN;
        break;
    }

    return idBitmap;
}

#if(_WIN32_WINNT >= 0x0500)

#include <dsrole.h>
BOOL IsStandalone(LPCTSTR pszMachine, PBOOL pbIsDC)
{
    BOOL bStandalone = TRUE;
    PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pDsRole = NULL;

    //
    // Find out if target machine is a standalone machine or joined to
    // an NT domain.
    //

    __try
    {
        if (pbIsDC)
            *pbIsDC = FALSE;

        DsRoleGetPrimaryDomainInformation(pszMachine,
                                          DsRolePrimaryDomainInfoBasic,
                                          (PBYTE*)&pDsRole);
    }
    __finally
    {
    }

    if (NULL != pDsRole)
    {
        if (pDsRole->MachineRole == DsRole_RoleStandaloneWorkstation ||
            pDsRole->MachineRole == DsRole_RoleStandaloneServer)
        {
            bStandalone = TRUE;
        }
        else
            bStandalone = FALSE;

        if (pbIsDC)
        {
            if (pDsRole->MachineRole == DsRole_RolePrimaryDomainController ||
                pDsRole->MachineRole == DsRole_RoleBackupDomainController)
            {
                *pbIsDC = TRUE;
            }
        }

        DsRoleFreeMemory(pDsRole);
    }

    return bStandalone;
}

#else   // _WIN32_WINNT < 0x0500

BOOL IsStandalone(LPCTSTR pszMachine, PBOOL pbIsDC)
{
    BOOL bStandalone = FALSE;

    //implement an NT4 version of this? There is no request for this
    //so no need.
    if (pbIsDC)
        *pbIsDC = FALSE;

    return bStandalone;
}


//
// Stuff used by GetUserGroup below
//
#include <getuser.h>

const TCHAR c_szGetUserLib[]        = TEXT("netui2.dll");
const char c_szOpenUB[]             = "OpenUserBrowser";
const char c_szEnumUBSelection[]    = "EnumUserBrowserSelection";
const char c_szCloseUB[]            = "CloseUserBrowser";

typedef HUSERBROW (WINAPI *PFN_UB_OPEN)(LPUSERBROWSER);
typedef BOOL (WINAPI *PFN_UB_ENUM)(HUSERBROW, LPUSERDETAILS, LPDWORD);
typedef BOOL (WINAPI *PFN_UB_CLOSE)(HUSERBROW);

PFN_UB_OPEN pfnUBOpen;
PFN_UB_ENUM pfnUBEnum;
PFN_UB_CLOSE pfnUBClose;

#ifndef HC_SED_USER_BROWSER_DIALOG
#define HC_SED_USER_BROWSER_DIALOG          4300
#define HC_SED_USER_BROWSER_AUDIT_DLG       4325
#endif


/*******************************************************************

    NAME:       GetUserGroup

    SYNOPSIS:   Invokes the old NT4 user/group picker dialog

    ENTRY:      hwndOwner - owner window
                dwFlags - indicate multi-select, etc.
                pszServer - initial focus of dialog
                ppUserList - out parameter

    EXIT:       *ppUserList contains a list of USER_INFO structures

    RETURNS:    HRESULT

    NOTES:      *ppUserList must be LocalFree'd by the caller.

    HISTORY:
        JeffreyS    16-Jan-1998     Created

********************************************************************/

HRESULT
GetUserGroup(HWND       hwndOwner,
             DWORD      dwFlags,
             LPCTSTR    pszServer,
             BOOL       /*bStandalone*/,
             PUSER_LIST *ppUserList)
{
    HRESULT hr = S_OK;
    HUSERBROW hUserBrowser = NULL;
    USERBROWSER ub;
    DWORD dwUDLength = 1024;
    PUSERDETAILS pUserDetails = NULL;
    PSID_CACHE_ENTRY pEntry;
    HDPA hEntryList = NULL;
    PSIDCACHE pSidCache = NULL;

    TraceEnter(TRACE_MISC, "GetUserGroup");
    TraceAssert(ppUserList != NULL);

    if (!ppUserList)
        TraceLeaveResult(E_INVALIDARG);

    *ppUserList = NULL;

    if (!g_hGetUserLib)
    {
        g_hGetUserLib = LoadLibrary(c_szGetUserLib);
        if (g_hGetUserLib == NULL)
            ExitGracefully(hr, E_FAIL, "Unable to load netui2.dll");

        pfnUBOpen = (PFN_UB_OPEN)GetProcAddress(g_hGetUserLib, c_szOpenUB);
        pfnUBEnum = (PFN_UB_ENUM)GetProcAddress(g_hGetUserLib, c_szEnumUBSelection);
        pfnUBClose = (PFN_UB_CLOSE)GetProcAddress(g_hGetUserLib, c_szCloseUB);

        if (!pfnUBOpen || !pfnUBEnum || !pfnUBClose)
        {
            FreeLibrary(g_hGetUserLib);
            g_hGetUserLib = NULL;
            ExitGracefully(hr, E_FAIL, "Unable to link to netui2.dll");
        }
    }

    //
    // Create the global sid cache object, if necessary
    //
    pSidCache = GetSidCache();

    if (pSidCache == NULL)
        ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create SID cache");

    ub.ulStructSize = sizeof(ub);
    ub.fUserCancelled = FALSE;
    ub.fExpandNames = TRUE;
    ub.hwndOwner = hwndOwner;
    ub.pszTitle = NULL;
    ub.pszInitialDomain = (LPTSTR)pszServer;
    ub.Flags = USRBROWS_SHOW_ALL | USRBROWS_INCL_ALL;
    ub.ulHelpContext = HC_SED_USER_BROWSER_DIALOG;
    ub.pszHelpFileName = (LPWSTR)c_szAcluiHelpFile;

#ifdef USRBROWS_INCL_RESTRICTED
    ub.Flags &= ~USRBROWS_INCL_RESTRICTED;  // NT5 only
#endif

    if (!(dwFlags & GU_CONTAINER))
        ub.Flags &= ~USRBROWS_INCL_CREATOR;

    if (!(dwFlags & GU_MULTI_SELECT))
        ub.Flags |= USRBROWS_SINGLE_SELECT;

    if (dwFlags & GU_AUDIT_HLP)
        ub.ulHelpContext = HC_SED_USER_BROWSER_AUDIT_DLG;

    //
    // Open the dialog
    //
    hUserBrowser = (*pfnUBOpen)(&ub);
    if (hUserBrowser == NULL)
        ExitGracefully(hr, E_FAIL, "OpenUserBrowser returned false");

    pUserDetails = (PUSERDETAILS)LocalAlloc(LPTR, dwUDLength);
    if (!pUserDetails)
        ExitGracefully(hr, E_OUTOFMEMORY, "Unable to allocate UserDetails buffer");

    hEntryList = DPA_Create(4);
    if (!hEntryList)
        ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create SID cache entry list");

    //
    // Enumerate the results
    //
    for (;;)
    {
        if (!(*pfnUBEnum)(hUserBrowser, pUserDetails, &dwUDLength))
        {
            if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
            {
                // The details buffer wasn't big enough, reallocate it
                LocalFree(pUserDetails);
                pUserDetails = (PUSERDETAILS)LocalAlloc(LPTR, dwUDLength);
                if (pUserDetails == NULL)
                    break;

                if (!(*pfnUBEnum)(hUserBrowser, pUserDetails, &dwUDLength))
                    break;
            }
            else // probably ERROR_NO_MORE_ITEMS
                break;
        }

        //
        // See if it's already in the cache
        //
        pEntry = pSidCache->FindSid(pUserDetails->psidUser);

        if (NULL == pEntry)
        {
            //
            // Not in the cache, add it
            //
            TCHAR szAccountName[MAX_PATH];
            TCHAR szDomainName[MAX_PATH];
            ULONG nAccountLength;

            lstrcpy(szAccountName, pUserDetails->pszAccountName);
            lstrcpy(szDomainName, pUserDetails->pszDomainName);

            switch (pUserDetails->UserType)
            {
            case SidTypeUnknown:
            case SidTypeInvalid:
                // Load unknown account string
                LoadString(::hModule, IDS_SID_UNKNOWN, szAccountName, ARRAYSIZE(szAccountName));
                break;

            case SidTypeAlias:
                //if (IsAliasSid(pSid))
                //    szDomainName[0] = TEXT('\0');   // The domain is "BUILTIN"
                break;

            case SidTypeDeletedAccount:
                // Load deleted account string
                LoadString(::hModule, IDS_SID_DELETED, szAccountName, ARRAYSIZE(szAccountName));
                break;

            case SidTypeWellKnownGroup:
                // Don't include the domain for a well-known group
                szDomainName[0] = TEXT('\0');
                break;
            }

            //
            // Build NT4 "domain\user" style name (logon name)
            //
            if (szDomainName[0] != TEXT('\0'))
            {
                lstrcat(szDomainName, TEXT("\\"));
                lstrcat(szDomainName, szAccountName);
            }

            LPCTSTR pszCommonName = pUserDetails->pszFullName;
            if (!pszCommonName || !*pszCommonName)
                pszCommonName = pUserDetails->pszAccountName;

            pEntry = pSidCache->MakeEntry(pUserDetails->psidUser,
                                          pUserDetails->UserType,
                                          pszCommonName,
                                          szDomainName);
            if (NULL != pEntry)
                pSidCache->AddEntry(pEntry);
        }

        if (NULL != pEntry)
            DPA_AppendPtr(hEntryList, pEntry);
    }

    //
    // Build return list
    //
    if (DPA_GetPtrCount(hEntryList))
        pSidCache->BuildUserList(hEntryList, pszServer, ppUserList);

    if (NULL == *ppUserList)
        hr = E_FAIL;

exit_gracefully:

    if (pSidCache)
        pSidCache->Release();

    if (NULL != hUserBrowser)
        (*pfnUBClose)(hUserBrowser);

    if (pUserDetails != NULL)
        LocalFree(pUserDetails);

    DPA_Destroy(hEntryList);

    TraceLeaveResult(hr);
}

#endif  // _WIN32_WINNT < 0x0500


/*******************************************************************

    NAME:       IsDACLCanonical

    SYNOPSIS:   Checks a DACL for canonical ordering

    ENTRY:      pDacl - points to DACL to check

    EXIT:

    RETURNS:    Nonzero if DACL is in canonical order, zero otherwise

    NOTES:

    HISTORY:
        JeffreyS    08-Oct-1996     Created
        JeffreyS    03-Oct-1997     Make object aces same as non-object aces

********************************************************************/

enum ACELEVEL
{
    alNonInheritAccessDenied,
    alNonInheritAccessAllowed,
    alInheritedAces,
};

BOOL
IsDACLCanonical(PACL pDacl)
{
    PACE_HEADER pAce;
    ACELEVEL currentAceLevel;
    DWORD dwAceCount;

    if (pDacl == NULL)
        return TRUE;

    currentAceLevel = alNonInheritAccessDenied;
    dwAceCount = pDacl->AceCount;

    if (dwAceCount == 0)
        return TRUE;

    for (pAce = (PACE_HEADER)FirstAce(pDacl);
         dwAceCount > 0;
         --dwAceCount, pAce = (PACE_HEADER)NextAce(pAce))
    {
        ACELEVEL aceLevel;

        //
        // NOTE: We do not skip INHERIT_ONLY aces because we want them in
        // canonical order too.
        //

        if (pAce->AceFlags & INHERITED_ACE)
        {
            aceLevel = alInheritedAces;      // don't check order here
        }
        else
        {
            switch(pAce->AceType)
            {
            case ACCESS_DENIED_ACE_TYPE:
            case ACCESS_DENIED_OBJECT_ACE_TYPE:
                aceLevel = alNonInheritAccessDenied;
                break;

            case ACCESS_ALLOWED_ACE_TYPE:
            case ACCESS_ALLOWED_COMPOUND_ACE_TYPE:
            case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
                aceLevel = alNonInheritAccessAllowed;
                break;

            default:
                return FALSE;
            }
        }

        //
        // If the ace type is less than the level we are currently at,
        // then it is not canonical.
        //
        if (aceLevel < currentAceLevel)
            return FALSE;

        //
        // Update the current ace level.
        //
        currentAceLevel = aceLevel;
    }

    //
    // If we get here, then the DACL is in canonical order.
    //
    return TRUE;
}


/*******************************************************************

    NAME:       IsDenyACL

    SYNOPSIS:   Checks a DACL for Deny ACEs.  Also looks for "Deny
                All" ACEs.

    ENTRY:      pDacl - points to DACL to check

    EXIT:       *pdwWarning is 0, IDS_PERM_DENY, or IDS_PERM_DENY_ALL 

    RETURNS:    Nonzero if DACL contains any Deny ACEs, zero otherwise

    NOTES:

    HISTORY:
        JeffreyS    05-Sep-1997     Created

********************************************************************/

BOOL
IsDenyACL(PACL pDacl,
          BOOL fProtected,
          DWORD dwFullControlMask,
          LPDWORD pdwWarning)
{
    DWORD dwWarning = 0;

    TraceEnter(TRACE_MISC, "IsDenyACL");

    // NULL DACL implies "Allow Everyone Full Control"
    if (pDacl == NULL)
        goto exit_gracefully;

    // Check for empty DACL (no access to anyone)
    if (pDacl->AceCount == 0)
    {
        if (fProtected)
            dwWarning = IDS_PERM_DENY_ALL;
        // else the object will inherit permissions from the parent.
    }
    else
    {
        PACE_HEADER pAce;
        int iEntry;

        // Iterate through the ACL looking for "Deny All"
        for (iEntry = 0, pAce = (PACE_HEADER)FirstAce(pDacl);
             iEntry < pDacl->AceCount;
             iEntry++, pAce = (PACE_HEADER)NextAce(pAce))
        {
            if (pAce->AceType != ACCESS_DENIED_ACE_TYPE &&
                pAce->AceType != ACCESS_DENIED_OBJECT_ACE_TYPE)
            {
                // Assuming the ACL is in canonical order, we can
                // stop as soon as we find something that isn't
                // a Deny ACE.  (Deny ACEs come first)
                break;
            }

            // Found a Deny ACE
            dwWarning = IDS_PERM_DENY;

            // Check for "Deny Everyone Full Control". Don't look
            // for ACCESS_DENIED_OBJECT_ACE_TYPE here since Object
            // aces don't have as wide an effect as normal aces.
            if (pAce->AceType == ACCESS_DENIED_ACE_TYPE &&
                ((PKNOWN_ACE)pAce)->Mask == dwFullControlMask &&
                EqualSid(GetAceSid(pAce), QuerySystemSid(UI_SID_World)))
            {
                // Found "Deny All"
                dwWarning = IDS_PERM_DENY_ALL;
                break;
            }
        }
    }

exit_gracefully:

    if (pdwWarning != NULL)
        *pdwWarning = dwWarning;

    TraceLeaveValue(dwWarning != 0);
}


/*******************************************************************

    NAME:       QuerySystemSid

    SYNOPSIS:   Retrieves the requested SID

    ENTRY:      SystemSidType - Which SID to retrieve

    EXIT:

    RETURNS:    PSID if successful, NULL otherwise

    HISTORY:
        JeffreyS    08-Oct-1996     Created

********************************************************************/

//
// Global array of static system SIDs, corresponding to UI_SystemSid
//
const struct
{
    SID sid;            // contains 1 subauthority
    DWORD dwSubAuth[1]; // we currently need at most 2 subauthorities
} g_StaticSids[COUNT_SYSTEM_SID_TYPES] =
{
    {{SID_REVISION,1,SECURITY_WORLD_SID_AUTHORITY,  {SECURITY_WORLD_RID}},              {0}                             },
    {{SID_REVISION,1,SECURITY_CREATOR_SID_AUTHORITY,{SECURITY_CREATOR_OWNER_RID}},      {0}                             },
    {{SID_REVISION,1,SECURITY_CREATOR_SID_AUTHORITY,{SECURITY_CREATOR_GROUP_RID}},      {0}                             },
    {{SID_REVISION,1,SECURITY_NT_AUTHORITY,         {SECURITY_DIALUP_RID}},             {0}                             },
    {{SID_REVISION,1,SECURITY_NT_AUTHORITY,         {SECURITY_NETWORK_RID}},            {0}                             },
    {{SID_REVISION,1,SECURITY_NT_AUTHORITY,         {SECURITY_BATCH_RID}},              {0}                             },
    {{SID_REVISION,1,SECURITY_NT_AUTHORITY,         {SECURITY_INTERACTIVE_RID}},        {0}                             },
    {{SID_REVISION,1,SECURITY_NT_AUTHORITY,         {SECURITY_SERVICE_RID}},            {0}                             },
    {{SID_REVISION,1,SECURITY_NT_AUTHORITY,         {SECURITY_ANONYMOUS_LOGON_RID}},    {0}                             },
    {{SID_REVISION,1,SECURITY_NT_AUTHORITY,         {SECURITY_PROXY_RID}},              {0}                             },
    {{SID_REVISION,1,SECURITY_NT_AUTHORITY,         {SECURITY_ENTERPRISE_CONTROLLERS_RID}},{0}                          },
    {{SID_REVISION,1,SECURITY_NT_AUTHORITY,         {SECURITY_PRINCIPAL_SELF_RID}},     {0}                             },
    {{SID_REVISION,1,SECURITY_NT_AUTHORITY,         {SECURITY_AUTHENTICATED_USER_RID}}, {0}                             },
    {{SID_REVISION,1,SECURITY_NT_AUTHORITY,         {SECURITY_RESTRICTED_CODE_RID}},    {0}                             },
    {{SID_REVISION,1,SECURITY_NT_AUTHORITY,         {SECURITY_TERMINAL_SERVER_RID}},    {0}                             },
    {{SID_REVISION,1,SECURITY_NT_AUTHORITY,         {SECURITY_LOCAL_SYSTEM_RID}},       {0}                             },
    {{SID_REVISION,2,SECURITY_NT_AUTHORITY,         {SECURITY_BUILTIN_DOMAIN_RID}},     {DOMAIN_ALIAS_RID_ADMINS}       },
    {{SID_REVISION,2,SECURITY_NT_AUTHORITY,         {SECURITY_BUILTIN_DOMAIN_RID}},     {DOMAIN_ALIAS_RID_USERS}        },
    {{SID_REVISION,2,SECURITY_NT_AUTHORITY,         {SECURITY_BUILTIN_DOMAIN_RID}},     {DOMAIN_ALIAS_RID_GUESTS}       },
    {{SID_REVISION,2,SECURITY_NT_AUTHORITY,         {SECURITY_BUILTIN_DOMAIN_RID}},     {DOMAIN_ALIAS_RID_POWER_USERS}  },
    {{SID_REVISION,2,SECURITY_NT_AUTHORITY,         {SECURITY_BUILTIN_DOMAIN_RID}},     {DOMAIN_ALIAS_RID_ACCOUNT_OPS}  },
    {{SID_REVISION,2,SECURITY_NT_AUTHORITY,         {SECURITY_BUILTIN_DOMAIN_RID}},     {DOMAIN_ALIAS_RID_SYSTEM_OPS}   },
    {{SID_REVISION,2,SECURITY_NT_AUTHORITY,         {SECURITY_BUILTIN_DOMAIN_RID}},     {DOMAIN_ALIAS_RID_PRINT_OPS}    },
    {{SID_REVISION,2,SECURITY_NT_AUTHORITY,         {SECURITY_BUILTIN_DOMAIN_RID}},     {DOMAIN_ALIAS_RID_BACKUP_OPS}   },
    {{SID_REVISION,2,SECURITY_NT_AUTHORITY,         {SECURITY_BUILTIN_DOMAIN_RID}},     {DOMAIN_ALIAS_RID_REPLICATOR}   },
    {{SID_REVISION,2,SECURITY_NT_AUTHORITY,         {SECURITY_BUILTIN_DOMAIN_RID}},     {DOMAIN_ALIAS_RID_RAS_SERVERS}  },
};

PSID
QuerySystemSid(UI_SystemSid SystemSidType)
{
    if (SystemSidType == UI_SID_Invalid || SystemSidType >= UI_SID_Count)
        return NULL;

    return (PSID)&g_StaticSids[SystemSidType];
}


//
// Global array of cached token SIDs
//
struct
{
    SID sid;            // SID contains 1 subauthority
    DWORD dwSubAuth[SID_MAX_SUB_AUTHORITIES - 1];
} g_TokenSids[COUNT_TOKEN_SID_TYPES] = {0};

PSID
QueryTokenSid(UI_TokenSid TokenSidType)
{
    if (TokenSidType == UI_TSID_Invalid || TokenSidType >= UI_TSID_Count)
        return NULL;

    if (0 == *GetSidSubAuthorityCount((PSID)&g_TokenSids[TokenSidType]))
    {
        HANDLE hProcessToken;

        // Get the current process's user's SID
        if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hProcessToken))
        {
            BYTE buffer[sizeof(TOKEN_USER) + sizeof(g_TokenSids[0])];
            ULONG cbBuffer = sizeof(buffer);

            switch (TokenSidType)
            {
            case UI_TSID_CurrentProcessUser:
                if (GetTokenInformation(hProcessToken,
                                        TokenUser,
                                        buffer,
                                        cbBuffer,
                                        &cbBuffer))
                {
                    PTOKEN_USER ptu = (PTOKEN_USER)buffer;
                    CopyMemory(&g_TokenSids[UI_TSID_CurrentProcessUser],
                               ptu->User.Sid,
                               GetLengthSid(ptu->User.Sid));
                }
                break;

            case UI_TSID_CurrentProcessOwner:
                if (GetTokenInformation(hProcessToken,
                                        TokenOwner,
                                        buffer,
                                        cbBuffer,
                                        &cbBuffer))
                {
                    PTOKEN_OWNER pto = (PTOKEN_OWNER)buffer;
                    CopyMemory(&g_TokenSids[UI_TSID_CurrentProcessOwner],
                               pto->Owner,
                               GetLengthSid(pto->Owner));
                }
                break;

            case UI_TSID_CurrentProcessPrimaryGroup:
                if (GetTokenInformation(hProcessToken,
                                        TokenPrimaryGroup,
                                        buffer,
                                        cbBuffer,
                                        &cbBuffer))
                {
                    PTOKEN_PRIMARY_GROUP ptg = (PTOKEN_PRIMARY_GROUP)buffer;
                    CopyMemory(&g_TokenSids[UI_TSID_CurrentProcessPrimaryGroup],
                               ptg->PrimaryGroup,
                               GetLengthSid(ptg->PrimaryGroup));
                }
                break;
            }
            CloseHandle(hProcessToken);
        }

        if (0 == *GetSidSubAuthorityCount((PSID)&g_TokenSids[TokenSidType]))
            return NULL;
    }

    return (PSID)&g_TokenSids[TokenSidType];
}


/*******************************************************************

    NAME:       GetAuthenticationID

    SYNOPSIS:   Retrieves the SID associated with the credentials
                currently being used for network access.
                (runas /netonly credentials)

    ENTRY:      pszServer = server on which to lookup the account.
                            NULL indicates local system.

    EXIT:

    RETURNS:    PSID if successful, NULL otherwise.  Caller must
                free with LocalFree.

    HISTORY:
        JeffreyS    05-Aug-1999     Created

********************************************************************/
PSID
GetAuthenticationID(LPCWSTR pszServer)
{
    PSID pSid = NULL;
    HANDLE hLsa;
    NTSTATUS Status;

    //
    // These LSA calls are delay-loaded from secur32.dll using the linker's
    // delay-load mechanism.  Therefore, wrap with an exception handler.
    //
    __try
    {
        Status = LsaConnectUntrusted(&hLsa);

        if (Status == 0)
        {
            NEGOTIATE_CALLER_NAME_REQUEST Req = {0};
            PNEGOTIATE_CALLER_NAME_RESPONSE pResp;
            ULONG cbSize;
            NTSTATUS SubStatus;

            Req.MessageType = NegGetCallerName;

            Status = LsaCallAuthenticationPackage(
                            hLsa,
                            0,
                            &Req,
                            sizeof(Req),
                            (void**)&pResp,
                            &cbSize,
                            &SubStatus);

            if ((Status == 0) && (SubStatus == 0))
            {
                BYTE sid[sizeof(SID) + (SID_MAX_SUB_AUTHORITIES - 1)*sizeof(DWORD)];
                PSID psid = (PSID)sid;
                DWORD cbSid = sizeof(sid);
                WCHAR szDomain[MAX_PATH];
                DWORD cchDomain = ARRAYSIZE(szDomain);
                SID_NAME_USE sidType;

                if (LookupAccountNameW(pszServer,
                                       pResp->CallerName,
                                       psid,
                                       &cbSid,
                                       szDomain,
                                       &cchDomain,
                                       &sidType))
                {
                    pSid = LocalAllocSid(psid);
                }

                LsaFreeReturnBuffer(pResp);
            }

            LsaDeregisterLogonProcess(hLsa);
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
    }

    return pSid;
}


/*******************************************************************

    NAME:       CopyUnicodeString

    SYNOPSIS:   Allocates a buffer and copies a string from
                a UNICODE_STRING sources.

    ENTRY:      pszDest - pointer to destination buffer
                cchDest - # of chars in pszDest (bytes for MBCS)
                pSrc - pointer to UNICODE_STRING to copy

    EXIT:       pszDest - containing copy of string

    RETURNS:    # of chars copied, or 0 if not successful.

    NOTES:

    HISTORY:
        JeffreyS    22-Jan-1998     Created

********************************************************************/

int
CopyUnicodeString(LPTSTR pszDest, ULONG cchDest, PLSA_UNICODE_STRING pSrc)
{
    int nResult;
    ULONG cchSrc;

    // If UNICODE, cchDest is size of destination buffer in chars
    // Else (MBCS) cchDest is size of destination buffer in bytes

    if (pszDest == NULL || 0 == cchDest)
        return 0;

    *pszDest = TEXT('\0');

    if (pSrc == NULL || pSrc->Buffer == NULL)
        return 0;

    // Get # of chars in source (not including NULL)
    cchSrc = pSrc->Length/sizeof(WCHAR);

#ifdef UNICODE
    //
    // Note that pSrc->Buffer may not be NULL terminated so we can't just
    // call lstrcpynW with cchDest.  Also, if we call lstrcpynW with cchSrc,
    // it copies the correct # of chars, but then overwrites the last char
    // with NULL giving an incorrect result.  If we call lstrcpynW with
    // (cchSrc+1) it reads past the end of the buffer, which may fault (360251)
    // causing lstrcpynW's exception handler to return 0 without NULL-
    // terminating the resulting string.
    //
    // So let's just copy the bits.
    //
    nResult = min(cchSrc, cchDest);
    CopyMemory(pszDest, pSrc->Buffer, sizeof(WCHAR)*nResult);
    if (nResult == (int)cchDest)
        --nResult;
    pszDest[nResult] = L'\0';
#else
    nResult = WideCharToMultiByte(CP_ACP,
                                  0,
                                  pSrc->Buffer,
                                  cchSrc,
                                  pszDest,
                                  cchDest,
                                  NULL,
                                  NULL);
#endif

    return nResult;
}


/*******************************************************************

    NAME:       CopyUnicodeString

    SYNOPSIS:   Allocates a buffer and copies a string from
                a UNICODE_STRING sources.

    ENTRY:      pSrc - pointer to UNICODE_STRING to copy

    EXIT:       *ppszResult - points to LocalAlloc'd buffer containing copy.

    RETURNS:    # of chars copied, or 0 if not successful.

    NOTES:

    HISTORY:
        JeffreyS    22-Jan-1998     Created

********************************************************************/

int
CopyUnicodeString(LPTSTR *ppszResult, PLSA_UNICODE_STRING pSrc)
{
    int nResult = 0;

    if (NULL == ppszResult)
        return 0;

    *ppszResult = NULL;

    if (NULL != pSrc)
    {
        ULONG cchResult;

        *ppszResult = NULL;

        // Get # of chars in source (including NULL)
        cchResult = pSrc->Length/SIZEOF(WCHAR) + 1;

        // Allocate buffer big enough for either UNICODE or MBCS result
        *ppszResult = (LPTSTR)LocalAlloc(LPTR, cchResult * 2);

        if (*ppszResult)
        {
            nResult = CopyUnicodeString(*ppszResult, cchResult, pSrc);

            if (0 == nResult)
            {
                LocalFree(*ppszResult);
                *ppszResult = NULL;
            }
        }
    }

    return nResult;
}


//
// Test GUIDs safely
//
BOOL IsSameGUID(const GUID *p1, const GUID *p2)
{
    BOOL bResult = FALSE;

    if (!p1) p1 = &GUID_NULL;
    if (!p2) p2 = &GUID_NULL;

    __try
    {
        bResult = InlineIsEqualGUID(*p1, *p2);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
    }

    return bResult;
}

/*******************************************************************

    NAME:       GetCountOfInheritableAces

    SYNOPSIS:   Get the count of aces in ACL which can be 
				inherited to child objects

    RETURNS:    Count of Aces

********************************************************************/
DWORD GetCountOfInheritableAces(PACL pAcl)
{
	if(!pAcl)
		return 0;

	DWORD dwCount = 0;
	PACE_HEADER pAce = NULL;
	int iEntry = 0;
	for (iEntry = 0, pAce = (PACE_HEADER)FirstAce(pAcl);
         iEntry < pAcl->AceCount;
         iEntry++, pAce = (PACE_HEADER)NextAce(pAce))
	{
			 //
			 //Consider only explicit aces
			 //
			 if((!(pAce->AceFlags & INHERITED_ACE))&&(pAce->AceFlags & (OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE)))
				 dwCount++;
	}
	
	return dwCount;
}
/*******************************************************************

    NAME:       GetCountOfInheritableAces

    SYNOPSIS:   Get the count of aces in SACL or DACL which can be 
				inherited to child objects

    RETURNS:    Count of Aces

********************************************************************/
DWORD GetCountOfInheritableAces(SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR pSD)
{
	if(!pSD)
		return 0;
	
	PACL pAcl = NULL;
	BOOL bPresent;
	BOOL bDefault;
	
	if(si & DACL_SECURITY_INFORMATION)
	{
		if(GetSecurityDescriptorDacl(pSD, &bPresent, &pAcl, &bDefault))
		{
			return GetCountOfInheritableAces(pAcl);
		}
	}
	else if(si & SACL_SECURITY_INFORMATION)
	{
		if(GetSecurityDescriptorSacl(pSD, &bPresent, &pAcl, &bDefault))
		{
			return GetCountOfInheritableAces(pAcl);
		}
	}

	return 0;
}

typedef struct AclBloatInfo{
	DWORD dwInheriteAceCount;
	SECURITY_INFORMATION si;
	HFONT hFont;
	BOOL bShowHelp;
}ACL_BLOAT_INFO;

INT_PTR CALLBACK
AclBloatDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_INITDIALOG:
        {
            ACL_BLOAT_INFO * pInfo= (ACL_BLOAT_INFO*)lParam;
			ASSERT(pInfo);
			SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)pInfo);
			
			//
			//Add a warning icon
			//
			// add the warning icon			
			HICON hWarn = LoadIcon(NULL, IDI_WARNING);
			SendDlgItemMessage(hDlg,  // dialog box window handle 
							   IDC_BLOAT_WARN_ICON,              // icon identifier 
							   STM_SETIMAGE,          // message to send 
							   (WPARAM) IMAGE_ICON,   // image type 
							   (LPARAM) hWarn); // icon handle 


			//
			//Set the title of dialog box
			//
			LPTSTR pszCaption = NULL;
			if(FormatStringID(&pszCaption,
							  ::hModule,
							  pInfo->si & DACL_SECURITY_INFORMATION ? IDS_PERMISSIONS : IDS_AUDITING))
			{
				SetWindowText(hDlg, pszCaption);
				LocalFreeString(&pszCaption);
			}

			//
			//Set the warning message
			//
			UINT cItem = pInfo->dwInheriteAceCount;
			WCHAR buffer[34];
			_itow(cItem,buffer,10);
			if(FormatStringID(&pszCaption,
							  ::hModule,
							  pInfo->si & DACL_SECURITY_INFORMATION ? IDS_ACLBLOAT_NO_LIST_LINE1:IDS_ACLBLOAT_NO_LIST_SACL_LINE1,
							  buffer))
			{
				SetDlgItemText(hDlg, IDC_ACLBLOAT_LINE1, pszCaption);
				LocalFreeString(&pszCaption);
			}

			//
			//make warning bold
			//
			MakeBold(GetDlgItem(hDlg,IDC_ACLB_WARNING), &(pInfo->hFont));

			//
			//Set the line2, hide the Help button and move other buttons.
			//
			if(!pInfo->bShowHelp)
			{
				if(FormatStringID(&pszCaption,
								  ::hModule,
								  pInfo->si & DACL_SECURITY_INFORMATION ? IDS_BLOAT_PERM_LINE2_NOHELP : IDS_BLOAT_AUDIT_LINE2_NOHELP))
				{
					SetDlgItemText(hDlg, IDC_ACLB_LINE3, pszCaption);
					LocalFreeString(&pszCaption);
				}
			
				RECT rcHelp, rcCancel;
				GetWindowRect(GetDlgItem(hDlg, IDHELP), &rcHelp);
				MapWindowPoints(NULL, hDlg, (LPPOINT)&rcHelp, 2);
				GetWindowRect(GetDlgItem(hDlg, IDCANCEL), &rcCancel);
				MapWindowPoints(NULL, hDlg, (LPPOINT)&rcCancel, 2);
				
				//
				//Hide the Help button, Move Cancel to help position 
				//and Ok to Cancel positon.
				//
				ShowWindow(GetDlgItem(hDlg, IDHELP),FALSE);				
				SetWindowPos(GetDlgItem(hDlg, IDCANCEL),
					         NULL,
						     rcHelp.left,
							 rcHelp.top,
							 0,
                             0,
                             SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
				
				SetWindowPos(GetDlgItem(hDlg, IDOK),
					         NULL,
						     rcCancel.left,
							 rcCancel.top,
							 0,
                             0,
                             SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);

			}

            break;
        }

        case WM_COMMAND:
        {
            WORD wControlID = GET_WM_COMMAND_ID(wParam, lParam);
            switch (wControlID)
            {
				case IDOK:
				{	
					ACL_BLOAT_INFO * pInfo = (ACL_BLOAT_INFO *)GetWindowLongPtr(hDlg, DWLP_USER);
					if(pInfo->hFont)
						DeleteObject(pInfo->hFont);
					pInfo->hFont = NULL;
					EndDialog(hDlg, FALSE);
					break;
				}
				case IDCANCEL:
				{
					ACL_BLOAT_INFO * pInfo = (ACL_BLOAT_INFO *)GetWindowLongPtr(hDlg, DWLP_USER);
					if(pInfo->hFont)
						DeleteObject(pInfo->hFont);
					pInfo->hFont = NULL;

					EndDialog(hDlg, TRUE);
					break;
				}

				case IDHELP:
					HtmlHelp(NULL,					
							 L"aclui.chm::/ACLUI_acl_BP.htm",
							 HH_DISPLAY_TOPIC,
							 0);
					return TRUE;				
		
            }
            break;
        }
    }
    return FALSE;
}

//
// This function displays the "An error has occured [Continue] [Cancel]" message
//
// Returns IDOK or IDCANCEL
//
BOOL
IsAclBloated(HWND hWndParent, SECURITY_INFORMATION si, DWORD dwInheritAceCount, int idd, BOOL bShowHelp)
{
	AclBloatInfo info;
	info.dwInheriteAceCount = dwInheritAceCount;
	info.si = si;
	info.hFont = NULL;
	info.bShowHelp = bShowHelp;
    return (BOOL)DialogBoxParam(::hModule,
                               MAKEINTRESOURCE(idd),
                               hWndParent,
                               AclBloatDialogProc,
                               (LPARAM)(&info));
}

BOOL IsAclBloated(HWND hDlg, SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR pSD, DWORD dwOrgInheritAceCount, BOOL bShowHelp)
{
	ASSERT(pSD);
	BOOL fReturn = FALSE;

	DWORD dwNewInheritAceCount = GetCountOfInheritableAces(si, pSD);
	if( ((int)dwNewInheritAceCount - (int)dwOrgInheritAceCount) > ACL_BLOAT_LIMIT )
		fReturn = IsAclBloated(hDlg, 
					           si,
							   dwNewInheritAceCount - dwOrgInheritAceCount,							   
							   si & DACL_SECURITY_INFORMATION ? IDD_BLOAT_NO_LIST : IDD_BLOAT_NO_LIST_SACL,
							   bShowHelp);

	return fReturn;
}

//
//Sets the font style to bold for the hwnd.
//phNewFont gets handle to newFont which
//is to freed after hwnd is destroyed.
//
HRESULT MakeBold (HWND hwnd, HFONT *phNewFont)
{
	HRESULT hr = S_OK;
	HFONT hFont = NULL;
	*phNewFont = NULL;
	LOGFONT LogFont;

	if(!hwnd || !phNewFont)
		return E_POINTER;


	hFont = (HFONT)SendMessage(hwnd,WM_GETFONT,0,0);
	if (!hFont)
	{
		hr = HRESULT_FROM_WIN32(GetLastError());
		return hr;
	}
    

	if (!GetObject(hFont,sizeof(LOGFONT),(LPVOID)(&LogFont)))
	{
		hr = HRESULT_FROM_WIN32(GetLastError());
		return hr;
	}

	LogFont.lfWeight = FW_BOLD;

	if (!(*phNewFont = CreateFontIndirect(&LogFont)))
	{
		hr = HRESULT_FROM_WIN32(GetLastError());
		return hr;
	}

	SendMessage(hwnd,WM_SETFONT,(WPARAM)(*phNewFont),MAKELPARAM(FALSE,0));

	return S_OK;	
}
