I'm posting my sample code that calls CreateProcessAsUser. As far as
I can tell I am enabling all necessary privileges before making the
call (according to the docs and other threads in various forums about
CreateProcessAsUser), but I keep getting error 1314, privilege not
held, when making the call. As you can see, I'm checking for errors
earlier on, and everything before that call is succeeding. This is on
a Windows XP Pro box with all the latest updates. When writing this I
was hoping simply to test loading the user's hive and environment
block (i.e. making sure it is available in the daughter process
correctly). But I can't even get the daughter process to start.
I'm lost at this point. If there is something obvious, pointers would
be hugely appreciated, thanks!
(By the way, I run test16 from an account with administrator
privileges, i.e. in the administrators group, and the testuser account
under which I try to run test17 is a plain user account. I don't
think I need to call ImpersonateLoggedOnUser, as the caller has higher
privileges than testuser, and the exe test17.exe is fully accessible
by any account).
[code parent process (test16)]
#include <windows.h>
#include <userenv.h>
#include <cstring>
#include <memory>
#include <iostream>
#pragma comment( lib, "userenv.lib" )
struct tLogonData
{
PROFILEINFO m_profileInfo;
HANDLE m_hImpersonate;
HANDLE m_hPrimary;
void* m_pUserEnvBlock;
bool m_valid;
// Structors.
tLogonData();
~tLogonData();
};
tLogonData::tLogonData()
: m_hImpersonate( 0 )
, m_hPrimary( 0 )
, m_pUserEnvBlock( 0 )
, m_valid( false )
{
std::memset( &m_profileInfo, 0, sizeof( PROFILEINFO ));
}
tLogonData::~tLogonData()
{
// We can unload the environment block and unload the profile.
if ( m_pUserEnvBlock != 0 )
DestroyEnvironmentBlock( m_pUserEnvBlock );
if ( m_profileInfo.hProfile != 0 )
UnloadUserProfile( m_hImpersonate, m_profileInfo.hProfile );
// Then can close the token handles.
if ( m_hPrimary != 0 )
CloseHandle( m_hPrimary );
if ( m_hImpersonate != 0 )
CloseHandle( m_hImpersonate );
} // ~tLogonData
static char const userName[] = "testuser";
static char const domainName[] = ".";
static char const password[] = "foobar99";
bool SetPrivilege(
HANDLE hToken, // access token handle
char const* privilege, // name of privilege to enable/disable
bool enable // to enable or disable privilege
)
{
TOKEN_PRIVILEGES tp;
LUID luid;
if (
!LookupPrivilegeValue(
NULL, // lookup privilege on local system
privilege, // privilege to lookup
&luid // receives LUID of privilege
)
)
{
printf("LookupPrivilegeValue error: %u\n", GetLastError() );
return false;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if ( enable )
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
if (
!AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL
)
)
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
return false;
}
return true;
}
int main( int, char** )
{
std::auto_ptr<tLogonData> p_logon_data( new tLogonData );
// Adjust the privilege levels of this process's token.
HANDLE process_token = 0;
if (
OpenProcessToken(
GetCurrentProcess(), TOKEN_ALL_ACCESS, &process_token
) == 0
)
{
unsigned long err = GetLastError();
return 1;
}
// Need several privileges.
if (
!SetPrivilege( process_token, SE_TCB_NAME, true ) ||
!SetPrivilege( process_token, SE_ASSIGNPRIMARYTOKEN_NAME, true )
||
!SetPrivilege( process_token, SE_INCREASE_QUOTA_NAME, true )
)
return 1;
// No longer need the token, close it.
CloseHandle( process_token );
if (
LogonUser(
&userName[ 0 ], &domainName[ 0 ], &password[ 0 ],
LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT,
&p_logon_data->m_hImpersonate
) == 0
)
return 1;
if (
DuplicateTokenEx(
p_logon_data->m_hImpersonate,
MAXIMUM_ALLOWED,
0,
SecurityImpersonation,
TokenPrimary,
&p_logon_data->m_hPrimary
) == 0
)
return 1;
p_logon_data->m_profileInfo.dwSize = static_cast<DWORD>( sizeof(
PROFILEINFO ));
p_logon_data->m_profileInfo.dwFlags = PI_NOUI;
p_logon_data->m_profileInfo.lpUserName = const_cast<char*>(
&userName[ 0 ] );
p_logon_data->m_profileInfo.lpProfilePath = 0; // FIXME: Should
call NetUserGetInfo for this, as might be roaming profile
if ( LoadUserProfile( p_logon_data->m_hImpersonate,
&p_logon_data->m_profileInfo ) == 0 )
return 1;
// Create the environment block that will be used when creating the
new
// mmb slave process(es).
if (
CreateEnvironmentBlock(
&p_logon_data->m_pUserEnvBlock, p_logon_data->m_hImpersonate,
false
) == 0
)
return 1;
// Next, attempt to read from the logged on user's hive.
HKEY root_key = static_cast<HKEY>(
p_logon_data->m_profileInfo.hProfile );
HKEY loc_key;
long res = RegOpenKeyEx(
root_key, "Software\\Terrex\\TestKey",
0, KEY_QUERY_VALUE, &loc_key
);
if ( res != ERROR_SUCCESS )
return 1;
// Get the string value that should be there.
char key_value[ 32 ];
unsigned long data_len = sizeof( key_value );
res = RegQueryValueEx(
loc_key, "testvalue", 0, 0,
reinterpret_cast<BYTE*>( &key_value[ 0 ] ),
&data_len
);
if ( res != ERROR_SUCCESS )
return 1;
// Dump out the value.
std::cout << "The testvalue from the registry is: " << &key_value[
0 ] << std::endl;
// Now attempt to start the external process as the indicated user.
STARTUPINFO startup_info;
PROCESS_INFORMATION process_info;
std::memset( &startup_info, 0, sizeof( startup_info ));
std::memset( &process_info, 0, sizeof( process_info ));
if (
CreateProcessAsUser(
p_logon_data->m_hPrimary,
"c:\\test17.exe", 0,
0, 0, false,
CREATE_NO_WINDOW | DETACHED_PROCESS |
CREATE_UNICODE_ENVIRONMENT,
p_logon_data->m_pUserEnvBlock, 0,
&startup_info, &process_info
) == 0
)
{
unsigned long err = GetLastError();
// DIES HERE WITH ERROR 1314!
return 1;
}
// Close the handles opened by this call.
CloseHandle( process_info.hProcess );
CloseHandle( process_info.hThread );
// Now attempt to destroy the logon data, closing everything.
delete p_logon_data.release();
// Sleep for 30 seconds.
Sleep( 30000 );
return 0;
} // main
[/code parent process]
[code daughter process (test17)]
#include <windows.h>
#include <fstream>
#include <cstdlib>
int main( int, char* argv[] )
{
std::ofstream file( "c:/temp/test17out.txt" );
file << "Running program " << argv[ 0 ] << std::endl;
// Attempt to look in registry for the key we placed there.
HKEY loc_key;
long res = RegOpenKeyEx(
HKEY_CURRENT_USER, "Software\\Terrex\\TestKey",
0, KEY_QUERY_VALUE, &loc_key
);
if ( res != ERROR_SUCCESS )
return 1;
char key_value[ 32 ];
unsigned long data_len = 32;
res = RegQueryValueEx(
loc_key, "testvalue", 0, 0,
reinterpret_cast<BYTE*>( &key_value[ 0 ] ),
&data_len
);
if ( res != ERROR_SUCCESS )
return 1;
// Dump it out.
file << "Found testvalue key, value is: " << &key_value[ 0 ] <<
std::endl;
// Finally, get the environment variable we stored in that user's
// environment.
char const* var_value = std::getenv( "TEST_VARIABLE" );
if ( var_value == 0 || var_value[ 0 ] == 0 )
file << "Unable to find TEST_VARIABLE in the environment." <<
std::endl;
else
file << "TEST_VARIABLE = " << var_value << std::endl;
return 0;
} // main
[/code daughter process]
-Eric Twietmeyer