I have a process handle with
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, THE_PROCESS_ID);
How can I get the username of the user that is running the process?
I am using unmanaged code (no .NET).
Use OpenProcessToken to get the token (obviously), then GetTokenInformation with the TokenOwner flag to get the SID of the owner. Then you can use LookupAccountSid to get the username.
if WMI is not an option, then use GetUserFromProcess below that takes the process ID as an input parameter and returns the user name and domain:
#include <comdef.h>
#define MAX_NAME 256
BOOL GetLogonFromToken (HANDLE hToken, _bstr_t& strUser, _bstr_t& strdomain)
{
DWORD dwSize = MAX_NAME;
BOOL bSuccess = FALSE;
DWORD dwLength = 0;
strUser = "";
strdomain = "";
PTOKEN_USER ptu = NULL;
//Verify the parameter passed in is not NULL.
if (NULL == hToken)
goto Cleanup;
if (!GetTokenInformation(
hToken, // handle to the access token
TokenUser, // get information about the token's groups
(LPVOID) ptu, // pointer to PTOKEN_USER buffer
0, // size of buffer
&dwLength // receives required buffer size
))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
goto Cleanup;
ptu = (PTOKEN_USER)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, dwLength);
if (ptu == NULL)
goto Cleanup;
}
if (!GetTokenInformation(
hToken, // handle to the access token
TokenUser, // get information about the token's groups
(LPVOID) ptu, // pointer to PTOKEN_USER buffer
dwLength, // size of buffer
&dwLength // receives required buffer size
))
{
goto Cleanup;
}
SID_NAME_USE SidType;
char lpName[MAX_NAME];
char lpDomain[MAX_NAME];
if( !LookupAccountSid( NULL , ptu->User.Sid, lpName, &dwSize, lpDomain, &dwSize, &SidType ) )
{
DWORD dwResult = GetLastError();
if( dwResult == ERROR_NONE_MAPPED )
strcpy (lpName, "NONE_MAPPED" );
else
{
printf("LookupAccountSid Error %u\n", GetLastError());
}
}
else
{
printf( "Current user is %s\\%s\n",
lpDomain, lpName );
strUser = lpName;
strdomain = lpDomain;
bSuccess = TRUE;
}
Cleanup:
if (ptu != NULL)
HeapFree(GetProcessHeap(), 0, (LPVOID)ptu);
return bSuccess;
}
HRESULT GetUserFromProcess(const DWORD procId, _bstr_t& strUser, _bstr_t& strdomain)
{
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,FALSE,procId);
if(hProcess == NULL)
return E_FAIL;
HANDLE hToken = NULL;
if( !OpenProcessToken( hProcess, TOKEN_QUERY, &hToken ) )
{
CloseHandle( hProcess );
return E_FAIL;
}
BOOL bres = GetLogonFromToken (hToken, strUser, strdomain);
CloseHandle( hToken );
CloseHandle( hProcess );
return bres?S_OK:E_FAIL;
}
WMI is probably the path of least resistance. You should also be able to get the token using OpenProcessToken, then GetTokenInformation to get the SID of the owner. You can then turn the SID into a user name.
WMI should be able to tell you that information. Otherwise you need to rely on undocumented fun in ntdll.dll. It appears others have found solutions that don't use ntdll.dll -- use them rather than undocumented stuff.
Here a solution knowing the process id.
std::optional<std::wstring> GetUserNameFromProcess(DWORD id)
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id); // 1- OpenProcess
std::wstring endUser = L"";
std::wstring endDomain = L"";
if (hProcess != NULL)
{
HANDLE hToken = NULL;
if (OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) // 2- OpenProcessToken
{
DWORD tokenSize = 0;
GetTokenInformation(hToken, TokenUser, NULL, 0, &tokenSize);
if (tokenSize > 0)
{
BYTE* data = new BYTE[tokenSize];
GetTokenInformation(hToken, TokenUser, data, tokenSize, &tokenSize); // 3- GetTokenInformation
TOKEN_USER* pUser = (TOKEN_USER*)data;
PSID pSID = pUser->User.Sid;
DWORD userSize = 0;
DWORD domainSize = 0;
SID_NAME_USE sidName;
LookupAccountSid(NULL, pSID, NULL, &userSize, NULL, &domainSize, &sidName);
wchar_t* user = new wchar_t[userSize + 1];
wchar_t* domain = new wchar_t[domainSize + 1];
LookupAccountSid(NULL, pSID, user, &userSize, domain, &domainSize, &sidName); // 4- LookupAccountSid
user[userSize] = L'\0';
domain[domainSize] = L'\0';
endUser = user;
endDomain = domain;
delete[] domain;
delete[] user;
delete[] data;
}
CloseHandle(hToken);
}
CloseHandle(hProcess);
if (endUser != L"")
return endUser;
}
return {};
}
Related
Here's CreateFile doc.
I want to create a file via CreateFile with SECURITY_ATTRIBUTES, when I create it under a windows account user A, the file shouldn't be accessed by another windows user B.
I found this
Creating a Security Descriptor for a New Object in C++
But still couldn't figure it out how to assgin a certain user.
But still couldn't figure it out how to assgin a certain user.
You need to get the SID of a certain user first.
Here is some steps,
Validate the input parameters.
Create buffers for the SID and the domain name that may be large
enough.
In a loop, call LookupAccountName to retrieve the SID for the
account name supplied. If the buffer for the SID or the buffer for
the domain name is not large enough, the buffer size needed is
returned in cbSid or cchDomainName, respectively, and a new buffer
is allocated before the next call to LookupAccountName. Note that
the information is retrieved on the local system when the
lpSystemName parameter is set to NULL.
Free the memory allocated for the domain name buffer.
Then pass the SID to the SetEntriesInAclA function,
The SetEntriesInAcl function creates a new access control list (ACL)
by merging new access control or audit control information into an
existing ACL structure.
Modified code:
#pragma comment(lib, "advapi32.lib")
#include <windows.h>
#include <stdio.h>
#include <aclapi.h>
#include <tchar.h>
#include <mq.h.>
HRESULT GetSid(
LPCWSTR wszAccName,
PSID* ppSid
)
{
// Validate the input parameters.
if (wszAccName == NULL || ppSid == NULL)
{
return MQ_ERROR_INVALID_PARAMETER;
}
// Create buffers that may be large enough.
// If a buffer is too small, the count parameter will be set to the size needed.
const DWORD INITIAL_SIZE = 32;
DWORD cbSid = 0;
DWORD dwSidBufferSize = INITIAL_SIZE;
DWORD cchDomainName = 0;
DWORD dwDomainBufferSize = INITIAL_SIZE;
WCHAR* wszDomainName = NULL;
SID_NAME_USE eSidType;
DWORD dwErrorCode = 0;
HRESULT hr = MQ_OK;
// Create buffers for the SID and the domain name.
*ppSid = (PSID) new BYTE[dwSidBufferSize];
if (*ppSid == NULL)
{
return MQ_ERROR_INSUFFICIENT_RESOURCES;
}
memset(*ppSid, 0, dwSidBufferSize);
wszDomainName = new WCHAR[dwDomainBufferSize];
if (wszDomainName == NULL)
{
return MQ_ERROR_INSUFFICIENT_RESOURCES;
}
memset(wszDomainName, 0, dwDomainBufferSize * sizeof(WCHAR));
// Obtain the SID for the account name passed.
for (; ; )
{
// Set the count variables to the buffer sizes and retrieve the SID.
cbSid = dwSidBufferSize;
cchDomainName = dwDomainBufferSize;
if (LookupAccountNameW(
NULL, // Computer name. NULL for the local computer
wszAccName,
*ppSid, // Pointer to the SID buffer. Use NULL to get the size needed,
&cbSid, // Size of the SID buffer needed.
wszDomainName, // wszDomainName,
&cchDomainName,
&eSidType
))
{
if (IsValidSid(*ppSid) == FALSE)
{
wprintf(L"The SID for %s is invalid.\n", wszAccName);
dwErrorCode = MQ_ERROR;
}
break;
}
dwErrorCode = GetLastError();
// Check if one of the buffers was too small.
if (dwErrorCode == ERROR_INSUFFICIENT_BUFFER)
{
if (cbSid > dwSidBufferSize)
{
// Reallocate memory for the SID buffer.
wprintf(L"The SID buffer was too small. It will be reallocated.\n");
FreeSid(*ppSid);
*ppSid = (PSID) new BYTE[cbSid];
if (*ppSid == NULL)
{
return MQ_ERROR_INSUFFICIENT_RESOURCES;
}
memset(*ppSid, 0, cbSid);
dwSidBufferSize = cbSid;
}
if (cchDomainName > dwDomainBufferSize)
{
// Reallocate memory for the domain name buffer.
wprintf(L"The domain name buffer was too small. It will be reallocated.\n");
delete[] wszDomainName;
wszDomainName = new WCHAR[cchDomainName];
if (wszDomainName == NULL)
{
return MQ_ERROR_INSUFFICIENT_RESOURCES;
}
memset(wszDomainName, 0, cchDomainName * sizeof(WCHAR));
dwDomainBufferSize = cchDomainName;
}
}
else
{
wprintf(L"LookupAccountNameW failed. GetLastError returned: %d\n", dwErrorCode);
hr = HRESULT_FROM_WIN32(dwErrorCode);
break;
}
}
delete[] wszDomainName;
return hr;
}
void main()
{
PSID sid;
GetSid(L"strives", &sid); // enter a user name
DWORD dwRes, dwDisposition;
PACL pACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
EXPLICIT_ACCESS ea;
SECURITY_ATTRIBUTES sa;
HANDLE lRes = NULL;
// Initialize an EXPLICIT_ACCESS structure for an ACE.
// The ACE will allow Everyone read access to the key.
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
ea.grfAccessPermissions = GENERIC_ALL;
ea.grfAccessMode = SET_ACCESS;
ea.grfInheritance = NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
ea.Trustee.ptstrName = (LPTSTR)sid;
// Create a new ACL that contains the new ACEs.
dwRes = SetEntriesInAcl(1, &ea, NULL, &pACL);
if (ERROR_SUCCESS != dwRes)
{
_tprintf(_T("SetEntriesInAcl Error %u\n"), GetLastError());
goto Cleanup;
}
// Initialize a security descriptor.
pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR,
SECURITY_DESCRIPTOR_MIN_LENGTH);
if (NULL == pSD)
{
_tprintf(_T("LocalAlloc Error %u\n"), GetLastError());
goto Cleanup;
}
if (!InitializeSecurityDescriptor(pSD,
SECURITY_DESCRIPTOR_REVISION))
{
_tprintf(_T("InitializeSecurityDescriptor Error %u\n"),
GetLastError());
goto Cleanup;
}
// Add the ACL to the security descriptor.
if (!SetSecurityDescriptorDacl(pSD,
TRUE, // bDaclPresent flag
pACL,
FALSE)) // not a default DACL
{
_tprintf(_T("SetSecurityDescriptorDacl Error %u\n"),
GetLastError());
goto Cleanup;
}
// Initialize a security attributes structure.
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;
// Use the security attributes to set the security descriptor
// when you create a key.
lRes = CreateFile(_T("D:\\File.txt"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
&sa, OPEN_ALWAYS, 0, NULL);
if (lRes != NULL)
{
_tprintf(_T("Create file success\n"));
}
Cleanup:
if (pACL)
LocalFree(pACL);
if (pSD)
LocalFree(pSD);
if (lRes)
CloseHandle(lRes);
return;
}
You can verify by checking the properties of the file.
Recently I came across a Windows API called GetAppContainerNamedObjectPath. But I have no idea on how I can use it.
I found a msdn page for this api (https://learn.microsoft.com/en-us/windows/win32/api/securityappcontainer/nf-securityappcontainer-getappcontainernamedobjectpath). But it does not have a right example and remarks, parameters are written poorly.
I am getting ERROR_INVALID_PARAMETER(87) error at the end, which tells me something's wrong with the parameters that I put. Here's what I've tried.
#define TokenIsAppContainer 29
#define TokenAppContainerSid 31
#define TokenAppContainerNumber 32
typedef struct _TOKEN_APPCONTAINER_INFORMATION {
PSID TokenAppContainer;
} TOKEN_APPCONTAINER_INFORMATION, *PTOKEN_APPCONTAINER_INFORMATION;
void GetAppContainerProcessInfo(CString & procName)
{
DWORD dwSize = 0;
DWORD dwResult;
HANDLE hToken;
PTOKEN_APPCONTAINER_INFORMATION pAppCoInfo;
WCHAR wcsDebug[1024] = {0,};
WCHAR * pwSID = NULL;
typedef BOOL (WINAPI *_LPGETAPPCONTAINERNAMEOBJECTPATH)(HANDLE, PSID, ULONG, LPWSTR, PULONG);
static _LPGETAPPCONTAINERNAMEOBJECTPATH lpGetAppContainerNamedObjectPath = NULL;
if (0 == lpGetAppContainerNamedObjectPath)
{
HMODULE hKernel32 = LoadLibraryExW(L"kernel32.dll", NULL, 0);
if (hKernel32)
{
lpGetAppContainerNamedObjectPath = reinterpret_cast<_LPGETAPPCONTAINERNAMEOBJECTPATH>(GetProcAddress(hKernel32, "GetAppContainerNamedObjectPath"));
}
}
if (lpGetAppContainerNamedObjectPath)
{
DWORD processId = (DWORD)_ttoi((LPCTSTR)procName);
//HANDLE hProcess = GetProcessHandleByProcessName(procName);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
if(!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
{
dwResult = GetLastError();
swprintf_s( wcsDebug, _countof(wcsDebug), L"OpenProcessToken Error(%u) PID(%d)\n", dwResult, processId );
AfxMessageBox(wcsDebug);
return;
}
if (!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS) TokenAppContainerSid, NULL, dwSize, &dwSize))
{
dwResult = GetLastError();
if( dwResult != ERROR_INSUFFICIENT_BUFFER )
{
swprintf_s( wcsDebug, _countof(wcsDebug), L"GetTokenInformation Error %u\n", dwResult );
AfxMessageBox(wcsDebug);
return;
}
}
pAppCoInfo = (PTOKEN_APPCONTAINER_INFORMATION) GlobalAlloc( GPTR, dwSize );
if (!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS) TokenAppContainerSid, pAppCoInfo, dwSize, &dwSize))
{
dwResult = GetLastError();
swprintf_s( wcsDebug, _countof(wcsDebug), L"GetTokenInformation Error %u\n", dwResult );
AfxMessageBox(wcsDebug);
return;
}
WCHAR wcsNamedObjectPath[MAX_PATH];
ULONG ulRetlen = 0;
BOOL bRet = lpGetAppContainerNamedObjectPath(hToken, pAppCoInfo->TokenAppContainer, _countof(wcsNamedObjectPath), wcsNamedObjectPath, &ulRetlen );
if (bRet)
{
swprintf_s( wcsDebug, _countof(wcsDebug), L"GetAppContainerNamedObjectPath Path(%s)\n", wcsNamedObjectPath );
AfxMessageBox(wcsDebug);
}
else
{
dwResult = GetLastError();
swprintf_s( wcsDebug, _countof(wcsDebug), L"GetAppContainerNamedObjectPath Error %u\n", dwResult );
AfxMessageBox(wcsDebug);
}
if (pwSID)
LocalFree(pwSID);
CloseHandle(hToken)
CloseHandle(hProcess);
}
}
As a side-note, I have tried using wchar_t * and dynamically allocate the memory buffer by calling GetAppContainerNamedObjectPath twice. But still had no chance. Return length does not return a meaningful value.
if you call RtlGetLastNtStatus(); instead GetLastError(); after GetAppContainerNamedObjectPath you will got
STATUS_INVALID_PARAMETER_MIX - An invalid combination of parameters was specified.
this give you more info compare simply invalid parameter.
then look for function signature
BOOL
WINAPI
GetAppContainerNamedObjectPath(
_In_opt_ HANDLE Token,
_In_opt_ PSID AppContainerSid,
_In_ ULONG ObjectPathLength,
_Out_writes_opt_(ObjectPathLength) LPWSTR ObjectPath,
_Out_ PULONG ReturnLength
);
the Token and AppContainerSid declared with In_opt -- this mean that this parameters is optional, and you can pass 0 in place one of it. then ask your self - for what you query token for TokenAppContainerSid ? are system can not do this for you if you pass this token to api ? obvious can. so you not need do this yourself. really you need pass Token to api and in this case AppContainerSid must be 0. or you can pass AppContainerSid to api and in this case Token must be 0. when both AppContainerSid and Token not zero - you and got STATUS_INVALID_PARAMETER_MIX
also as side note - you not need open process with PROCESS_ALL_ACCESS if you need get it token. the PROCESS_QUERY_LIMITED_INFORMATION is enough
really api not do big magic. it return to you
AppContainerNamedObjects\<Sid>
path, where string form of app container sid.(some like S-1-15-2-...)
I am attempting to create a service that will essentially act as a local web server. In theory users will use a REST API by visiting URIs through localhost in a browser i.e. http://localhost:2017/path/to/function/call will connect to the service and execute a function.
My question is how do I get the SID and User Name of the account that called the service?
I have implemented a couple of solutions but they return the SID and User Name of the service and not the user using it.
OJSon* UnifiedStreamingService::getUserDetails()
{
OJSon* result = OJSon::create();
if(result)
{
/*
HANDLE hToken = NULL;
ULONG id = WTSGetActiveConsoleSessionId();
BOOL bRet = WTSQueryUserToken(id, &hToken);
if (bRet == false)
{
DWORD error = GetLastError();
printf("ERROR: %d", error);
}
*/
HANDLE hToken = NULL;
if ( ! OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &hToken ) )
{
//_tprintf( _T("OpenProcessToken failed. GetLastError returned: %d\n"), GetLastError());
return NULL;
}
// Get the size of the memory buffer needed for the SID
DWORD dwBufferSize = 0;
if ( ! GetTokenInformation( hToken, TokenUser, NULL, 0, &dwBufferSize ) && ( GetLastError() != ERROR_INSUFFICIENT_BUFFER ) )
{
//_tprintf( _T("GetTokenInformation failed. GetLastError returned: %d\n"), GetLastError());
// Cleanup
CloseHandle( hToken );
hToken = NULL;
return NULL;
}
// Allocate buffer for user token data
std::vector<BYTE> buffer;
buffer.resize( dwBufferSize );
PTOKEN_USER pTokenUser = reinterpret_cast<PTOKEN_USER>( &buffer[0] );
// Retrieve the token information in a TOKEN_USER structure
if ( ! GetTokenInformation(
hToken,
TokenUser,
pTokenUser,
dwBufferSize,
&dwBufferSize))
{
//_tprintf( _T("2 GetTokenInformation failed. GetLastError returned: %d\n"), GetLastError());
// Cleanup
CloseHandle( hToken );
hToken = NULL;
return NULL;
}
// Check if SID is valid
if ( ! IsValidSid( pTokenUser->User.Sid ) )
{
//_tprintf( _T("The owner SID is invalid.\n") );
// Cleanup
CloseHandle(hToken);
hToken = NULL;
return NULL;
}
// add the name
OString* name = lookupAccountSid(pTokenUser->User.Sid);
if(name)
{
result->setKey(&OString("name"), name);
SAFEDELETE(name);
}
// add the SID
OString* sid = convertSidToString(pTokenUser->User.Sid);
if(sid)
{
result->setKey(&OString("SID"), sid);
SAFEDELETE(sid);
}
// Cleanup
CloseHandle(hToken);
hToken = NULL;
}
return result;
}
OString* UnifiedStreamingService::convertSidToString(PSID pSID)
{
OString* result = NULL;
if(pSID)
{
// Get string corresponding to SID
LPTSTR pszSID = NULL;
if ( ! ConvertSidToStringSid( pSID, &pszSID ) )
{
return NULL;
}
result = new OString(pszSID);
// Release buffer allocated by ConvertSidToStringSid API
LocalFree( pszSID );
pszSID = NULL;
}
return result;
}
OString* UnifiedStreamingService::lookupAccountSid(PSID pSID)
{
DWORD dwSize = 256;
DWORD dwResult = 0;
SID_NAME_USE SidType;
LPTSTR lpName = new TCHAR[dwSize];
LPWSTR lpDomain = new TCHAR[dwSize];
OString* result = NULL;
if( !LookupAccountSid( NULL, pSID, lpName, &dwSize, lpDomain, &dwSize, &SidType ) )
{
dwResult = GetLastError();
return NULL;
}
OString* pDomain = new OString(lpDomain);
OString* pName = new OString(lpName);
if(pDomain && pName)
{
result = OString::createByFormat(&OString("%s\\%s"), pDomain, pName);
SAFEDELETE(pDomain);
SAFEDELETE(pName);
}
delete[] lpDomain;
delete[] lpName;
return result;
}
The task can be accomplished by using WTSGetActiveConsoleSessionId and WTSQueryUserToken to get user token and then getting SID with GetTokenInformation.
The additional requirement is the service is running under Local System account which grants SE_TCB_NAME privelege (== SeTcbPrivilege). SE_TCB_NAME required by WTSQueryUserToken. Note that the other accounts usually have no SE_TCB_NAME privelege!
I'm using OpenProcessToken, GetTokenInformation and then LookupAccountSid to determine the owner of a certain process.
On a local machine (Win 7 and Win 8.1), on a RD Services session (Server 2012) it works fine. I do get the correct user name. The user name displayed in the task manager next to the process.
When I execute the same code in a Provisioning (ex Citrix) environment I only get the username "Administrator" although there is a different name displayed in the task manager.
Does anybody have an idea how to conquer this within a Provisioning environment?
Thanks a lot for any help
Martin
Here is the C++ Code I'm using:
BOOL DDEWinWord::processStartedFromLocalUser(DWORD procId)
{
#define MAX_NAME 256
DWORD dwSize = 0, dwResult = 0;
HANDLE hToken;
SID_NAME_USE SidType;
char lpName[MAX_NAME];
char lpDomain[MAX_NAME];
PTOKEN_OWNER tp;
// Open a handle to the access token for the calling process.
HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procId);
if (!OpenProcessToken(processHandle, TOKEN_QUERY, &hToken)) {
AfxMessageBox("processStartedFromLocalUser - OpenProcessToken fehlschlag.");
return FALSE;
}
// Call GetTokenInformation to get the buffer size.
if(!GetTokenInformation(hToken, TokenOwner, NULL, dwSize, &dwSize))
{
dwResult = GetLastError();
if (dwResult != ERROR_INSUFFICIENT_BUFFER)
{
AfxMessageBox("processStartedFromLocalUser - GetTokenInformation fehlschlag.");
return FALSE;
}
}
// Allocate the buffer.
tp = (PTOKEN_OWNER)GlobalAlloc(GPTR, dwSize);
// Call GetTokenInformation again to get the group information.
if (!GetTokenInformation(hToken, TokenOwner, tp, dwSize, &dwSize))
{
AfxMessageBox("processStartedFromLocalUser - GetTokenInformation mit tp fehlschlag.");
return FALSE;
}
if (!LookupAccountSid(NULL, tp->Owner, lpName, &dwSize, lpDomain, &dwSize, &SidType))
{
AfxMessageBox("processStartedFromLocalUser - LookupAccountSid fehlschlag.");
return FALSE;
}
else
{
AfxMessageBox(lpName);
}
return (m_stUserId.CompareNoCase(lpName) == 0);
}
You should be using TokenUser rather than TokenOwner.
I need to log off a user from a C++ program. I use ExitWindowsEx API for that, but I'm not sure from the documentation if I need any special privileges for that?
You do. Here's an example
bool ShutdownWindows(void)
{
HANDLE hToken = NULL;
TOKEN_PRIVILEGES tkp = {0};
bool bRet = false;
// Get a token for this process.
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
if (LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid)) {
tkp.PrivilegeCount = 1; // one privilege to set
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// Get the shutdown privilege for this process.
if (AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0)) {
::CloseHandle(hToken);
if (ERROR_SUCCESS == GetLastError()) {
DWORD dwFlags = EWX_POWEROFF;
DWORD dwReason = SHTDN_REASON_MAJOR_SYSTEM;
if (ExitWindowsEx(dwFlags, dwReason)) {
bRet = true;
}
}
}
}
}
return bRet;
} // ShutdownWindows