Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

How Can I Launch a Secondary Process From My Application with Different User Privileges?


How Can I Launch a Secondary Process From My Application with Different User Privileges?


Many applications need to interact with third party or other external systems already installed on a user’s computer. If you’re lucky, the external system provides support for COM Automation, OLE, or DDE to facilitate interprocess communication. This can make the job relatively straightforward under Windows. Unfortunately, it is arguable that the majority of applications do not provide a robust means for other applications to communicate with them. As one who has developed many applications, I think it is safe to say that the reason is simple: time (or lack thereof). Support for external access is often way down on the list of priorities from the Product Management/Marketing group, unless it is a key part of the application’s focus.

However, from the beginning, Windows (and DOS before it) have provided ways to launch an application programmatically. When the C runtime was the toolkit most developers turned to for reusable items, we had the spawn family of functions. These provided various ways to launch an executable from a fully specified filename or one that was relative and used the PATH environment variable to locate a matching file. You could pass parameters, and the like, and receive error codes back if something went wrong—such as if the application couldn’t be found.

In the pre-Win95 (Win16) world, we had the WinExec function, which looked like this:

	UINT WinExec(
	  LPCSTR Error! Hyperlink reference not valid.,
	  UINT Error! Hyperlink reference not valid.
	);

Basically, you could think of it as a Windows version of the spawn functions. The return value indicated success or failure and not much else. Like the spawn functions, it would check the PATH variable, but also check much more including the Windows and Windows System directories. Many applications that were originally written for Win16 and later ported to Win32 still use WinExec rather than newer Win32 versions, and it's not uncommon to see new code using it.

When the Win32 world arrived with Windows 95, a whole new class of API's became available-process management. Instead of just launching applications, we could now manage their lifetimes, determine the context they ran in, and so on. The core function of this new API is CreateProcess(), which provides a similar baseline capability as WinExec (launching executables) but greatly expands the set of capabilities as you can see below:

BOOL CreateProcess(
  LPCTSTR Error! Hyperlink reference not valid.,
  LPTSTR Error! Hyperlink reference not valid.,
  LPSECURITY_ATTRIBUTES Error! Hyperlink reference not valid.,
  LPSECURITY_ATTRIBUTES Error! Hyperlink reference not valid.,
  BOOL Error! Hyperlink reference not valid.,
  DWORD Error! Hyperlink reference not valid.,
  LPVOID Error! Hyperlink reference not valid.,
  LPCTSTR Error! Hyperlink reference not valid.,
  LPSTARTUPINFO Error! Hyperlink reference not valid.,
  LPPROCESS_INFORMATION Error! Hyperlink reference not valid.
);

Besides the path, we can now control the security attributes, where it is launched from, how the process should appear visually, whether the process should be created as a console application, suspended when first executed, and so on. The return values also get more elaborate in the PROCESS_INFORMATION structure that includes such things as the process handle, primary thread handle, process ID and thread ID. This ensures that the caller knows exactly what process (among the running ones) was created via the call. This can make tasks such as waiting for a process to complete much simpler when you are sure you are waiting on the right process.

Within the CreateProcess family of functions, there are a couple of additional ones along with some helper functions that provide the ability to run a process under a different set of user credentials. At this point, if you have never had a reason to do such a thing, you might be wondering why someone would want to do this. If all of your logins are Administrator level in your environment (unfortunately this can be quite common), then there is likely little reason to do this other than to ensure that logging or other user-credential facilities within the application are fooled into using the correct account. Usually a process is run with different credentials because the workstation login does not have sufficient credentials to perform the task. Consider a network administrator needing to install an application on a user's computer who is running under the basic Users group with minimal privileges (this is a routine case in a corporate environment). The network admin could logout the user and relogin as the Administrator. This works but could be inconvenient if the user has applications running and work unsaved. An alternative would be to run the setup application itself under an alternative account.

Some of you may be familiar with the RunAs command available within Windows to do just such a thing. Executing this command from the command line shows the following:

Looking at the various parameters, we see that we can specify both a path to an executable and an account (called a "user). One of the limitations with this utility (or strengths depending on your point of view) is that the password for the user account cannot be included in the command line-the utility prompts for the password. This utility is a way that the network admin could run that setup application with different credentials easily and simply.

So now we know it's both possible to create such code under Windows and desirable. We'll now take a look at some code that shows how:

#include <windows.h>

// logon as user "Administrator" as a local account with password // "12345678"
HANDLE hToken = NULL;
if ( LogonUser( "Administrator", NULL, "12345678", 
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,&hToken ) )
{
		PROCESS_INFORMATION processInfo;
		ZeroMemory(&processInfo,sizeof(processInfo));

		STARTUPINFO info;
		ZeroMemory(&info,sizeof(info));
		info.wShowWindow = SW_SHOW;
		if ( CreateProcessAsUser( hToken, NULL, 
				"c:\\windows\\notepad.exe", NULL, NULL, TRUE, 
				CREATE_NEW_CONSOLE, NULL, NULL, &info, 
				&processInfo) )
		{
			// if we get here, the process was created 
// successfully with the specified credentials.

			//... do some work.. 

			// wait for the application to terminate
			WaitForSingleObject( processInfo.hProcess,INFINITE );
		}

		CloseHandle(hToken);
}

Looking at this code, we see that we first create a login token using LogonUser and then launch the specified process with a call to CreateProcessAsUser together with the token. Hopefully, you can see that running processes under various user credentials is both a necessary thing on occasion and quite possible with Win32.

Side Dish

In the example, you will notice that I pass a hardwired string as the password. You would most likely pass a string array or pointer to a string as the value. After doing so (and assuming the pointer is not intended to be read-only), you should call the API SecureZeroMemory, which safely (and securely) zeros out the memory used by the pointer. The function can't be optimized out of code like a call to the more familiar ZeroMemory function can be. Since you're passing a plain-text password around, calling this function ensures that the password text is not left laying around in memory for a sniffer program to find.


Mark M. Baker is the Chief of Research & Development at BNA Software located in Washington, D.C.
Do you have a Windows development question? Send it to [email protected].


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.