Re: LogonUser / CreateProcessAsUser - Behaves differently from different calling applications

From: Richard Ward (richardw_at_delete-yellow-dogs.com)
Date: 05/05/04


Date: Wed, 5 May 2004 02:05:12 -0700

Since you're not supplying a lpDesktop field in the startup info,
the new process will be in a non-interactive desktop and session.
Different runtimes may handle that differently.

"Colin" <anonymous@discussions.microsoft.com> wrote in message
news:B24EAA42-526F-4B9C-BB6A-3807618DD887@microsoft.com...
>I have written a small win32 DLL that will switch the user context for an
>application and execute a simple command. I accomplish this using
>LogonUser, ImpersonateLoggedOnUser, and DuplicateTokenEx and then call
>CreateProcessAsUser to execute the command.
>
> I am experiencing some odd behaviour. Calling the DLL from another win32
> console application compiled under visual studio and the DLL works fine
> and executes the command as the new user. However, if I call the DLL from
> a win32 console application compiled using Watcom C++ (an old compiler
> originally meant for WINNT 4.0) I get a security violation "Access Is
> Denied" when I try to execute the command. However, if use the
> administrator account as the new user, it executes fine.
>
> I have tried giving my test user the exact same priveliges as the
> administrator in the local policy settings but I still get the access
> denied violation.
>
> Why do two applications using the same DLL behave differently just because
> they are compiled under different compilers'
>
> p.s. I have to get this working using watcom as our products are legacy
> products and use the watcom compiler for now.
>
> The code for the DLL is below:
>
>
> /*------------------- OBJECT DEFINITIONS -------------------*/
>
> /*
> * This object maintains data for the Authentication and Process
> * control methods in this DLL library.
> */
> typedef struct _ProcessState
> {
> DWORD dwExitCode; // Exit code of the last executed process
> command
> } ProcessState;
>
>
> /*------------------- PROTOTYPES -------------------*/
> void reportError();
> LPVOID appendToEnvironmentBlock( LPSTR asExtraVars[] );
> BOOL freeEnvironmentBlock( LPVOID pLocalEnv );
>
>
>
> /*------------------- MEMBERS -------------------*/
>
> static ProcessState mProcessState; // state object for
> Authentication & Process control functions
>
>
>
>
> /**
> * Main entry point for DLL. Initialization and Destruction
> * code should be inserted here.
> *
> * @param hModule DLL Handle
> * @param ul_reason_for_call
> * Reason for call e.g. Init, Unload, etc
> * @param lpReserved Reserver
> *
> * @return True on success
> */
> BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID
> lpReserved )
> {
>
> switch ( ul_reason_for_call )
> {
> case DLL_PROCESS_ATTACH:
> memset( &mProcessState, 0, sizeof( mProcessState ) );
> break;
> case DLL_THREAD_ATTACH:
> break;
> case DLL_THREAD_DETACH:
> break;
> case DLL_PROCESS_DETACH:
> break;
> }
>
> return (TRUE);
> }
>
>
>
> /**
> * Authenticate a user using the provided credentials. If authentication
> * is successful. Switch the current process credentials and
> * impersonate the user. This is used to apply user domain security
> * for a remote service. Return a system handle the caller can use
> * in other calls to win32 that require a user context token.
> *
> * @param sUser user name
> * @param sPassword user password (Clear Text)
> * @param sDomain Domain to authenticate against
> *
> * @return Non null on success
> * Null if the authentication failed. If null is returned, the
> * caller should call GetLastError() to find the specific error
> * which occurred.
> */
> HANDLE _stdcall switchUser( LPCSTR sUser, LPCSTR sPassword, LPCSTR
> sDomain )
> {
> HANDLE hLogonToken, hImpersonationToken;
> BOOL bSuccess;
>
> /*----------------------*/
>
> try
> {
>
> hLogonToken = 0;
> bSuccess = LogonUser( sUser, sDomain, sPassword,
> LOGON32_LOGON_NETWORK_CLEARTEXT,
> LOGON32_PROVIDER_WINNT50, &hLogonToken );
>
> if ( !bSuccess )
> throw "Logon User failed\n";
>
> bSuccess = ImpersonateLoggedOnUser( hLogonToken );
>
> if ( !bSuccess )
> throw "Impersonation failed\n";
>
> bSuccess = DuplicateTokenEx( hLogonToken, MAXIMUM_ALLOWED, 0,
> SecurityImpersonation, TokenPrimary, &hImpersonationToken );
>
> if ( !bSuccess )
> throw "Duplicate token failed\n";
>
> }
> catch ( char * sErr )
> {
> std::cout << sErr;
> reportError();
> }
>
> return (hImpersonationToken);
>
> }
>
>
> /**
> * Execute a command in it's own thread. This call is blocking while
> * the command executes. This function requires a handle to the current
> * authentication token to spawn the thread in the same user context.
> * This is useful for remote services providing domain security access,
> * the thread is spawned as the user with their security access.
> *
> * @param hUserToken Handle for a user token
> * @param sCommandLine
> * command line to execute e.g. "cmd.exe /C dir >
> dir.txt"
> * @param asExtraEnvironmentVars
> * Array of string pointers to custom environment
> variables.
> * These variable will be appended to the current
> environment
> * for the executing child process.
> * @param iTimeOut integer time out value in milliseconds. Time to wait
> for
> * command to complete
> *
> * @return True on success
> * False if the process can not be successfully spawned. Callers
> * should call GetLastError to find the precise error if the
> function
> * fails.
> *
> * If the function succeeds, it does not mean the command was
> successful,
> * only that the thread was spawned and the command ran to
> completion
> * To get the exit code of the command, call getCommandExitCode().
> This
> * will return the exit code of the last executed command.
> */
> BOOL _stdcall execCommand( HANDLE hUserToken, LPCSTR sCommandLine, LPSTR
> asExtraEnvironmentVars[], INT iTimeOut )
> {
> BOOL bSuccess;
> DWORD dwStatus, dwExitCode;
> LPVOID sUserEnv;
>
> STARTUPINFO startupInfo;
> PROCESS_INFORMATION processInfo;
>
> /*----------------------*/
>
> try
> {
> memset( &startupInfo, 0, sizeof( startupInfo ) );
> memset( &processInfo, 0, sizeof( processInfo ) );
> sUserEnv = NULL;
>
> if ( asExtraEnvironmentVars != NULL )
> {
> // Create new environment block with local vars added
> sUserEnv = appendToEnvironmentBlock( asExtraEnvironmentVars );
> }
>
> bSuccess = CreateProcessAsUser( hUserToken, 0, (LPSTR )sCommandLine,
> 0, 0, 0, 0, sUserEnv, 0, &startupInfo, &processInfo );
>
> if ( !bSuccess )
> throw "CreateProcessAsUser failed\n";
>
>
> CloseHandle( processInfo.hThread ); // close pipe handle -
> child's instance
> dwStatus = WaitForSingleObject( processInfo.hProcess, iTimeOut );
> std::cout << "WaitForSingleObject rc = " << dwStatus << endl;
>
> switch ( dwStatus )
> {
> case WAIT_ABANDONED:
> std::cout << "Wait abandoned" << endl;
> break;
>
> case WAIT_OBJECT_0:
> std::cout << "Object signaled" << endl;
> break;
>
> case WAIT_TIMEOUT:
> std::cout << "Timeout" << endl;
> break;
>
> default:
> std::cout << "Unknown result" << endl;
> break;
> }
>
> bSuccess = GetExitCodeProcess( processInfo.hProcess, &dwExitCode );
> CloseHandle( processInfo.hProcess ); // close process handle or
> it won't die
>
> if ( !bSuccess )
> throw "Get exit code failed\n";
>
> std::cout << "Exit Code: " << dwExitCode << endl;
>
> mProcessState.dwExitCode = dwExitCode;
> return bSuccess;
> }
> catch ( char * sErr )
> {
> std::cout << sErr;
> reportError();
> mProcessState.dwExitCode = -1;
> return false;
> }
> }
>
>
> /**
> * Get the exit code of the last command executed using one
> * of the process control commands (e.g. execCommand() )
> *
> * @return DWORD value of the exit code
> */
> DWORD _stdcall getCommandExitCode()
> {
> return mProcessState.dwExitCode;
> }
>
>
>



Relevant Pages

  • RE: error after installing MCE update rollup 2
    ... thanks for helping me figure out which dll i needed, i have no idea why i ... that install and copy it to where it needs to be in my current install, ... Execute the command regsvr32.exe atl.dll ...
    (microsoft.public.windows.mediacenter)
  • dll for xp_startmail
    ... I dropped the stored procedure "xp_startmail". ... Can you tell me what dll xp_startmail is located in so I can execute a command something like the following: ...
    (microsoft.public.sqlserver.setup)
  • [Full-Disclosure] Advanced usage of system() function.
    ... and call its arguments as a command for shell. ... as we can see we still didnt get what we want (typing exit ... Connection closed by foreign host. ... think what we want to execute. ...
    (Full-Disclosure)
  • Advanced usage of system() function.
    ... and call its arguments as a command for shell. ... as we can see we still didnt get what we want (typing exit we are ... Connection closed by foreign host. ... think what we want to execute. ...
    (Bugtraq)
  • Re: Wait for background processes to complete
    ... To be able to execute commands in the background and wait for their ... The documentation I am referring to is http://perldoc.perl.org/. ... You can run a command in the background with: ... There is a general problem with perl documentation: ...
    (comp.lang.perl.misc)

Loading