//+------------------------------------------------------------------------- // // 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 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 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; }