Desktop problem with using CreateProcessAsUser from a service on Vista
Solution 1
After lots of googling i am able to find the solution for my problem.
Here is the link where i have found the solution. http://www.codeproject.com/KB/vista-security/interaction-in-vista.aspx
Thanks every one who tried to help me.
Solution 2
Here is complete working code to do what you want
//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);
}
Solution 3
What is initiating the service to create a new process as that has bearing on what a suitable answer is? WTSGetActiveConsoleSessionId gets the session ID of the current physical console session, not the "active" one in your sense. With terminal services there might be many different users all connected to different sessions so you cannot just pick one and hope, but there may or may not actually be a physical logged on user.
One way is to use WTSEnumerateSessions to list all active sessions and try and find the one you actually want, you can use something like WTSQuerySessionInformation to get the username for that session.
If your service is doing this in response to some request from your user account (rather than acting on some automatic event) then if you are using something like RPC/Named Pipes/DCOM etc. you can always impersonate the user (assuming security is set to allow non-anonymous/identify impersonation) and duplicate the thread's token into a primary token and use that.
coolcake
Updated on June 08, 2022Comments
-
coolcake almost 2 years
I am using VC++ to create a process from a service on Vista using CreateProcessAsUser. The process creation is succeeding when i execute this code by login on the machine locally. The same code is failing when I am using Remote Desktop (mstsc) to login in to my machine from another machine and launch my App.
I can understand that when I do remote login the active desktop is taken as that of Remote Desktop. Can some one help me how to get the desktop name that of currently login users desktop and not that of Remote desktop.
Here is my code
ACTIVECONSOLESESSIONIDFUNC lpfnProc; // WTSGetActiveConsoleSessionId function pointer HMODULE hModule = NULL; // Instance for kernel32.dll library DWORD dwSessionId = 0; // Session ID HANDLE hToken = NULL; // Active session token HANDLE hDupToken = NULL; // Duplicate session token WCHAR szErr[1024] = {0}; LPVOID lpEnvironment = NULL; // Environtment block // Get the active session ID hModule = LoadLibrary(KERNEL32LIB); if(!hModule) { //wsprintf(szErr, L"LoadLibrary Error: %d", GetLastError()); return; } lpfnProc = (ACTIVECONSOLESESSIONIDFUNC)GetProcAddress(hModule,"WTSGetActiveConsoleSessionId"); dwSessionId = lpfnProc(); // Get token of the logged in user by the active session ID BOOL bRet = WTSQueryUserToken(dwSessionId, &hToken); if (!bRet) { //wsprintf(szErr, L"WTSQueryUserToken Error: %d", GetLastError()); return; } // Get duplicate token from the active logged in user's token bRet = DuplicateTokenEx(hToken, // Active session token MAXIMUM_ALLOWED, // Desired access NULL, // Token attributes SecurityIdentification, // Impersonation level TokenPrimary, // Token type &hDupToken); // New/Duplicate token if (!bRet) { //wsprintf(szErr, L"DuplicateTokenEx Error: %d", GetLastError()); return; } // Get all necessary environment variables of logged in user // to pass them to the process bRet = CreateEnvironmentBlock(&lpEnvironment, // Environment block hDupToken, // New token FALSE); // Inheritence if(!bRet) { //wsprintf(szErr, L"CreateEnvironmentBlock Error: %d", GetLastError()); return; } HDESK hdeskInput=OpenInputDesktop(0, FALSE, 0); // does not set GetLastError(), so GetLastError() is arbitrary if NULL is returned if( hdeskInput==NULL ) { TRACE( "hdeskInput==NULL" ); return false; } // Initialize Startup and Process info startupInfo->cb = sizeof(STARTUPINFO); startupInfo->lpDesktop = TEXT("winsta0\\default"); // Start the process on behalf of the current user BOOL returnCode = CreateProcessAsUser(hDupToken, applicationName, commandLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, lpEnvironment, NULL, startupInfo, &processInformation);
Thanks, F
-
coolcake about 14 yearsThank you Tyranid. What ever the code I have pasted above is the one which I have got by browsing the net. I do not have any knowledge on desktops or sessions. I hope you have understood my problem. My problem is i shall login to my desktop from my machine then the application is behaving properly ie. i am able to create process from service but when i login into my desktop from another machine(mstsc) my service is not able to create the process. Because it is failing in the call WtsQueryUserToken with error 1008 ERROR_NO_TOKEN; session id i am getting is 3, while my actual is 1. Please help.
-
tyranid about 14 yearsAs said what is it which is actually creating the process, as in how does the code above get called and in what context.
-
coolcake about 14 yearsMy App has a service which is running under system account creates the process. I want create a separate process to handle the GUI related tasks of the service. When my service receives pipe message from the App tries to create the process.
-
tyranid about 14 yearsOf course I hope you are the only one on that machine otherwise you could still randomly create a process on another desktop :)
-
coolcake about 14 yearsSo if i need to solve the problem even in that case. What should i need to do?
-
Jimmy Obonyo Abor over 7 yearsVery well put , kudos !