I have this problem that happened once and I still don't know how to fix it. I have a windows service, when the service runs, it first need to impersonate the logged in user (active user) to load some paths and settings that are saved in the user's application data folder. The code that I'm using works perfectly every time a new user logs on to windows except once where the service got the impersonation wrong and impersonated the system session instead of the actie one. As I said this only happened once, but I can't really tell why.
This is how am checking whats the active session and how the impersonation is done:
first when the service detects a logon event it query's the active session ID by calling
WTSGetActiveConsoleSessionId();
then It checks if the session is active(connected) by calling WTSQuerySessionInformation as follows:
WTS_CONNECTSTATE_CLASS wts_connect_state = WTSDisconnected;
WTS_CONNECTSTATE_CLASS* ptr_wts_connect_state = NULL;
DWORD bytes_returned = 0;
if (::WTSQuerySessionInformation(
WTS_CURRENT_SERVER_HANDLE,
session_id,
WTSConnectState,
reinterpret_cast<LPTSTR*>(&ptr_wts_connect_state),
&bytes_returned))
{
ASSERT(bytes_returned == sizeof(*ptr_wts_connect_state));
wts_connect_state = *ptr_wts_connect_state;
::WTSFreeMemory(ptr_wts_connect_state);
return (WTSActive == wts_connect_state);
}
where session_id is the session ID returned by WTSGetActiveConsoleSessionId().
Then I query for the user token using WTSQueryUserToken
Then if that succeeds the service calls GetTokenInformation
as follows:
DWORD neededSize = 0;
HANDLE *realToken = new HANDLE;
if(GetTokenInformation(hImpersonationToken, (::TOKEN_INFORMATION_CLASS) TokenLinkedToken, realToken, sizeof(HANDLE), &neededSize))
{
CloseHandle(hImpersonationToken);
hImpersonationToken = *realToken;
}
where hImpersonationToken is the token retrieved from GetTokenInformation
And if all the above succeeds it then calls
DuplicateTokenEx( hImpersonationToken,
0,
NULL,
SecurityImpersonation,
TokenPrimary,
phUserToken );
CloseHandle( hImpersonationToken );
and if it succeeds then it impersonates with the retrieved token
ImpersonateLoggedOnUser(phUserToken);
My service writes to a log file and according to the log all the previous calls where successful yet after the impersonation the service loaded the system profile instead of the user.
Now this issue happened once when I restarted my machine, yet I wasn't even reproduce it again and I've been trying for weeks.
I'm not sure how its possible for the system profile session to be an active session. I just want to know if I'm doing something wrong there, not sure if I'm using the wrong info class when I'm querying the session info or something.
Also want to know if its possible to determine if the queried session is actually the system session before impersonating with the returned token just so one can retry the impersonation again?
As I said, all mentioned calls have their return objects and codes checked before moving to the next step so their weren't any errors from the calls as it shouldn't continue with the impersonation, yet it did :(
Would appreciate any help possible... thanks.
WTSGetActiveConsoleSessionId() may actually return session 0 when run from a service and depending on the Windows version it runs on.
The way to do what you want is enumerate all sessions find the one that is connected (session 0 is disconnected) and then impersonate the user of that session. Code below works well on my box.
//Function to run a process as active user from windows service
void ImpersonateActiveUserAndRun(WCHAR* path, WCHAR* args)
{
DWORD session_id = -1;
DWORD session_count = 0;
WTS_SESSION_INFOA *pSession = NULL;
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSession, &session_count))
{
//log success
}
else
{
//log error
return;
}
for (int i = 0; i < session_count; i++)
{
session_id = pSession[i].SessionId;
WTS_CONNECTSTATE_CLASS wts_connect_state = WTSDisconnected;
WTS_CONNECTSTATE_CLASS* ptr_wts_connect_state = NULL;
DWORD bytes_returned = 0;
if (::WTSQuerySessionInformation(
WTS_CURRENT_SERVER_HANDLE,
session_id,
WTSConnectState,
reinterpret_cast<LPTSTR*>(&ptr_wts_connect_state),
&bytes_returned))
{
wts_connect_state = *ptr_wts_connect_state;
::WTSFreeMemory(ptr_wts_connect_state);
if (wts_connect_state != WTSActive) continue;
}
else
{
//log error
continue;
}
HANDLE hImpersonationToken;
if (!WTSQueryUserToken(session_id, &hImpersonationToken))
{
//log error
continue;
}
//Get real token from impersonation token
DWORD neededSize1 = 0;
HANDLE *realToken = new HANDLE;
if (GetTokenInformation(hImpersonationToken, (::TOKEN_INFORMATION_CLASS) TokenLinkedToken, realToken, sizeof(HANDLE), &neededSize1))
{
CloseHandle(hImpersonationToken);
hImpersonationToken = *realToken;
}
else
{
//log error
continue;
}
HANDLE hUserToken;
if (!DuplicateTokenEx(hImpersonationToken,
//0,
//MAXIMUM_ALLOWED,
TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS | MAXIMUM_ALLOWED,
NULL,
SecurityImpersonation,
TokenPrimary,
&hUserToken))
{
//log error
continue;
}
// Get user name of this process
//LPTSTR pUserName = NULL;
WCHAR* pUserName;
DWORD user_name_len = 0;
if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session_id, WTSUserName, &pUserName, &user_name_len))
{
//log username contained in pUserName WCHAR string
}
//Free memory
if (pUserName) WTSFreeMemory(pUserName);
ImpersonateLoggedOnUser(hUserToken);
STARTUPINFOW StartupInfo;
GetStartupInfoW(&StartupInfo);
StartupInfo.cb = sizeof(STARTUPINFOW);
//StartupInfo.lpDesktop = "winsta0\\default";
PROCESS_INFORMATION processInfo;
SECURITY_ATTRIBUTES Security1;
Security1.nLength = sizeof SECURITY_ATTRIBUTES;
SECURITY_ATTRIBUTES Security2;
Security2.nLength = sizeof SECURITY_ATTRIBUTES;
void* lpEnvironment = NULL;
// Get all necessary environment variables of logged in user
// to pass them to the new process
BOOL resultEnv = CreateEnvironmentBlock(&lpEnvironment, hUserToken, FALSE);
if (!resultEnv)
{
//log error
continue;
}
WCHAR PP[1024]; //path and parameters
ZeroMemory(PP, 1024 * sizeof WCHAR);
wcscpy(PP, path);
wcscat(PP, L" ");
wcscat(PP, args);
// Start the process on behalf of the current user
BOOL result = CreateProcessAsUserW(hUserToken,
NULL,
PP,
//&Security1,
//&Security2,
NULL,
NULL,
FALSE,
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,
//lpEnvironment,
NULL,
//"C:\\ProgramData\\some_dir",
NULL,
&StartupInfo,
&processInfo);
if (!result)
{
//log error
}
else
{
//log success
}
DestroyEnvironmentBlock(lpEnvironment);
CloseHandle(hImpersonationToken);
CloseHandle(hUserToken);
CloseHandle(realToken);
RevertToSelf();
}
WTSFreeMemory(pSession);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With