diff --git a/Contrib/UserInfo/UserInfo.c b/Contrib/UserInfo/UserInfo.c index fbc438bc..87047714 100644 --- a/Contrib/UserInfo/UserInfo.c +++ b/Contrib/UserInfo/UserInfo.c @@ -1,5 +1,7 @@ #include #include "../ExDLL/exdll.h" +typedef BOOL (WINAPI*CHECKTOKENMEMBERSHIP)(HANDLE TokenHandle,PSID SidToCheck,PBOOL IsMember); +CHECKTOKENMEMBERSHIP _CheckTokenMembership=NULL; void __declspec(dllexport) GetName(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) @@ -17,102 +19,127 @@ void __declspec(dllexport) GetName(HWND hwndParent, int string_size, } } +char* GetAccountTypeHelper(BOOL CheckTokenForGroupDeny) +{ + char *group; + HANDLE hToken = NULL; + struct group + { + DWORD auth_id; + char *name; + }; + + struct group groups[] = + { + {DOMAIN_ALIAS_RID_USERS, "User"}, + // every user belongs to the users group, hence users come before guests + {DOMAIN_ALIAS_RID_GUESTS, "Guest"}, + {DOMAIN_ALIAS_RID_POWER_USERS, "Power"}, + {DOMAIN_ALIAS_RID_ADMINS, "Admin"} + }; + + if (GetVersion() & 0x80000000) // Not NT + { + return "Admin"; + } + + // First we must open a handle to the access token for this thread. + if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken) || + OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) + { + SID_IDENTIFIER_AUTHORITY SystemSidAuthority = {SECURITY_NT_AUTHORITY}; + TOKEN_GROUPS *ptg = NULL; + BOOL ValidTokenGroups = FALSE; + DWORD cbTokenGroups; + DWORD i, j; + + + if (CheckTokenForGroupDeny) + // GetUserName is in advapi32.dll so we can avoid Load/Freelibrary + _CheckTokenMembership= + (CHECKTOKENMEMBERSHIP) GetProcAddress( + GetModuleHandle("ADVAPI32"), "CheckTokenMembership"); + + // Use "old school" membership check? + if (!CheckTokenForGroupDeny || _CheckTokenMembership == NULL) + { + // We must query the size of the group information associated with + // the token. Note that we expect a FALSE result from GetTokenInformation + // because we've given it a NULL buffer. On exit cbTokenGroups will tell + // the size of the group information. + if (!GetTokenInformation(hToken, TokenGroups, NULL, 0, &cbTokenGroups) && + GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + // Allocate buffer and ask for the group information again. + // This may fail if an administrator has added this account + // to an additional group between our first call to + // GetTokenInformation and this one. + if ((ptg = GlobalAlloc(GPTR, cbTokenGroups)) && + GetTokenInformation(hToken, TokenGroups, ptg, cbTokenGroups, &cbTokenGroups)) + { + ValidTokenGroups=TRUE; + } + } + } + + if (ValidTokenGroups || (CheckTokenForGroupDeny && _CheckTokenMembership)) + { + PSID psid; + for (i = 0; i < sizeof(groups)/sizeof(struct group); i++) + { + // Create a SID for the local group and then check if it exists in our token + if (AllocateAndInitializeSid( + &SystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, + groups[i].auth_id, 0, 0, 0, 0, 0, 0,&psid)) + { + BOOL IsMember = FALSE; + if (CheckTokenForGroupDeny && _CheckTokenMembership) + { + _CheckTokenMembership(0, psid, &IsMember); + } + else if (ValidTokenGroups) + { + for (j = 0; j < ptg->GroupCount; j++) + { + if (EqualSid(ptg->Groups[j].Sid, psid)) + { + IsMember = TRUE; + } + } + } + + if (IsMember) group=groups[i].name; + FreeSid(psid); + } + } + } + + if (ptg) + GlobalFree(ptg); + CloseHandle(hToken); + + return group; + } + + return ""; +} + void __declspec(dllexport) GetAccountType(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) { EXDLL_INIT(); + pushstring(GetAccountTypeHelper(TRUE)); +} - { - HANDLE hThread; - TOKEN_GROUPS *ptg = NULL; - DWORD cbTokenGroups; - DWORD i, j; - - SID_IDENTIFIER_AUTHORITY SystemSidAuthority = {SECURITY_NT_AUTHORITY}; - - char *group = ""; - - if (GetVersion() & 0x80000000) // Not NT - { - group = "Admin"; - } - - // First we must open a handle to the access token for this thread. - - else if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hThread) || - OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hThread)) - { - // Then we must query the size of the group information associated with - // the token. Note that we expect a FALSE result from GetTokenInformation - // because we've given it a NULL buffer. On exit cbTokenGroups will tell - // the size of the group information. - - if (!GetTokenInformation (hThread, TokenGroups, NULL, 0, &cbTokenGroups) && - GetLastError() == ERROR_INSUFFICIENT_BUFFER) - { - - // Now we allocate a buffer for the group information. - // Since _alloca allocates on the stack, we don't have - // to explicitly deallocate it. That happens automatically - // when we exit this function. - - if ((ptg = GlobalAlloc(GPTR, cbTokenGroups))) - { - - // Now we ask for the group information again. - // This may fail if an administrator has added this account - // to an additional group between our first call to - // GetTokenInformation and this one. - - if (GetTokenInformation(hThread, TokenGroups, ptg, cbTokenGroups, &cbTokenGroups)) - { - - struct group - { - DWORD auth_id; - char *name; - } groups[] = { - {DOMAIN_ALIAS_RID_USERS, "User"}, - // every user belongs to the users group, hence users comes before guests - {DOMAIN_ALIAS_RID_GUESTS, "Guest"}, - {DOMAIN_ALIAS_RID_POWER_USERS, "Power"}, - {DOMAIN_ALIAS_RID_ADMINS, "Admin"} - }; - - // Finally we'll iterate through the list of groups for this access - // token looking for a match against the SID we created above. - - for (i = 0; i < sizeof(groups)/sizeof(struct group); i++) - { - PSID psid = 0; - AllocateAndInitializeSid( - &SystemSidAuthority, - 2, - SECURITY_BUILTIN_DOMAIN_RID, - groups[i].auth_id, - 0, 0, 0, 0, 0, 0, - &psid - ); - if (psid == 0) continue; - for (j = 0; j < ptg->GroupCount; j++) - if (EqualSid(ptg->Groups[j].Sid, psid)) - group = groups[i].name; - FreeSid(psid); - } - } - - GlobalFree(ptg); - } - } - - CloseHandle(hThread); - } - - pushstring(group); - } +void __declspec(dllexport) GetOriginalAccountType(HWND hwndParent, int string_size, + char *variables, stack_t **stacktop) +{ + EXDLL_INIT(); + pushstring(GetAccountTypeHelper(FALSE)); } BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) { - return TRUE; + return TRUE; } + diff --git a/Contrib/UserInfo/UserInfo.nsi b/Contrib/UserInfo/UserInfo.nsi index 06090879..e05a1617 100644 --- a/Contrib/UserInfo/UserInfo.nsi +++ b/Contrib/UserInfo/UserInfo.nsi @@ -1,6 +1,8 @@ Name "UserInfo.dll test" OutFile UserInfo.exe +!define REALMSG "$\nOriginal non-restricted account type: $2" + Section ClearErrors UserInfo::GetName @@ -8,17 +10,26 @@ Section Pop $0 UserInfo::GetAccountType Pop $1 + # GetOriginalAccountType will check the tokens of the original user of the + # current thread/process. If the user tokens were elevated or limited for + # this process, GetOriginalAccountType will return the non-restricted + # account type. + # On Vista with UAC, for example, this is not the same value when running + # with `RequestExecutionLevel user`. GetOriginalAccountType will return + # "admin" while GetAccountType will return "user". + UserInfo::GetOriginalAccountType + Pop $2 StrCmp $1 "Admin" 0 +3 - MessageBox MB_OK 'User "$0" is in the Administrators group' + MessageBox MB_OK 'User "$0" is in the Administrators group${REALMSG}' Goto done StrCmp $1 "Power" 0 +3 - MessageBox MB_OK 'User "$0" is in the Power Users group' + MessageBox MB_OK 'User "$0" is in the Power Users group${REALMSG}' Goto done StrCmp $1 "User" 0 +3 - MessageBox MB_OK 'User "$0" is just a regular user' + MessageBox MB_OK 'User "$0" is just a regular user${REALMSG}' Goto done StrCmp $1 "Guest" 0 +3 - MessageBox MB_OK 'User "$0" is a guest' + MessageBox MB_OK 'User "$0" is a guest${REALMSG}' Goto done MessageBox MB_OK "Unknown error" Goto done @@ -29,4 +40,5 @@ Section MessageBox MB_OK "Error! This DLL can't run under Windows 9x!" done: -SectionEnd \ No newline at end of file +SectionEnd +