CreateProcessAsUser

I

Ian Boyd

An application running in a Windows 2000 enterprise by standard domain users
needs to launch an external application as a different domain user. i'm
looking for a functional code sample that calls CreateProcessAsUser.

i've not been able to find any sample code, or write my own, that doesn't
fail with error 1314.

The function prototype that everyone who ever calls CreateProcessAsUser is:
boolean CreateProcessAsUserEx(string Username, Password, Domain,
ApplicationName, CommandLine);


Can someone please reply with one of the following:
a) functional code body of CreateProcessAsUserEx
b) a statement saying that it is not possible to call CreateProcessAsUser
from a standard user account
 
S

Skywing [MVP]

I assume that you are really failing at a call to LogonUser. On Windows
2000 (and below), LogonUser requires SeTcbPrivilege. On Windows XP or
later, it can be used by a plain user account with no privileges.

On Windows 2000, you may be able to use CreateProcessWithLogonW, which takes
no special privileges. This is essentially how runas (secondary logon) on
Windows 2000 works.
 
I

Ian Boyd

Skywing [MVP]" said:
I assume that you are really failing at a call to LogonUser. On Windows
2000 (and below), LogonUser requires SeTcbPrivilege.

Is there a way to grant SeTcbPrivlege (Act as part of the operating system)
to a domain account without granting SeTcbPrivlege to the domain account?
Obviously i'm not going to grant every user in the global enterprise the
ability to act as part of the operating system.

On Windows XP or later, it can be used by a plain user account with no
privileges.

On Windows XP, the call to LogonUser succeeds, but the call to
CreateProcessAsUser fails with code 1314 (A required privilege is not held
by the client).

On Windows 2000, you may be able to use CreateProcessWithLogonW, which
takes no special privileges. This is essentially how runas (secondary
logon) on Windows 2000 works.

This does seem to work with my most likely incorrect code, but here's the
code anyway:

function ExecuteFileAsUser(
const Username, Domain, Password: WideString;
const FileName: WideString;
const Params: WideString='';
const DefaultDir: WideString ='';
const ShowCmd: Integer=SW_SHOWNORMAL): Boolean;
var
LocallyModifiableParams: WideString;
startupInfo: TStartupInfo;
processInformation: PROCESS_INFORMATION;
const
LOGON_WITH_PROFILE = $00000001;
begin
{
Sample Usage:
ExecuteFileAsUser(
'forest', //user name
'goldshire', //domain name
'gump', //password
'c:\windows\system32\notepad.exe', //filename
'c:\20070608.log', //params
'c:\develop' //default dir
);
}

LocallyModifiableParams := Filename+' '+Params;
{A command line contains the executable name, e.g.
notepad.exe c:\somefile.txt
}

{From the Platform SDK:
The function can modify the contents of this string.
Therefore, this parameter cannot be a pointer to read-only memory
(such as a const variable or a literal string).
If this parameter is a constant string, the function may cause an
access violation.

ed: A function that tries to cause access violations. Nice.}

//An empty structure that the function wants
ZeroMemory(@startupInfo, SizeOf(startupInfo));
startupInfo.cb :=SizeOf(startupInfo);

//An empty structure that the function wants
ZeroMemory(@processInformation, SizeOf(processInformation));


Result := CreateProcessWithLogonW(
PWideChar(Username),
PWideChar(Domain),
PWideChar(Password),
LOGON_WITH_PROFILE, //dwLogonFlags: DWORD
PWideChar(Filename),
PWideChar(LocallyModifiableParams),
0, //dwCreationFlags: DWORD
nil, //lpEnvironment: Pointer
PWideChar(DefaultDir), //lpCurrentDirectory: PWideChar;
startupInfo,
processInformation);
if Result then
begin
{From the Platform SDK:
Handles in PROCESS_INFORMATION must be closed with the
CloseHandle
function when they are not needed.}

//We don't need either of the handles in ProcessInformation.
//Hopefully Microsoft won't add any more to the structure in
the future
CloseHandle(processInformation.hProcess);
CloseHandle(processInformation.hThread);
end;
end;
 
K

Kellie Fitton

Is there a way to grant SeTcbPrivlege (Act as part of the operating system)
to a domain account without granting SeTcbPrivlege to the domain account?
Obviously i'm not going to grant every user in the global enterprise the
ability to act as part of the operating system.


On Windows XP, the call to LogonUser succeeds, but the call to
CreateProcessAsUser fails with code 1314 (A required privilege is not held
by the client).


This does seem to work with my most likely incorrect code, but here's the
code anyway:

function ExecuteFileAsUser(
const Username, Domain, Password: WideString;
const FileName: WideString;
const Params: WideString='';
const DefaultDir: WideString ='';
const ShowCmd: Integer=SW_SHOWNORMAL): Boolean;
var
LocallyModifiableParams: WideString;
startupInfo: TStartupInfo;
processInformation: PROCESS_INFORMATION;
const
LOGON_WITH_PROFILE = $00000001;
begin
{
Sample Usage:
ExecuteFileAsUser(
'forest', //user name
'goldshire', //domain name
'gump', //password
'c:\windows\system32\notepad.exe', //filename
'c:\20070608.log', //params
'c:\develop' //default dir
);

}

LocallyModifiableParams := Filename+' '+Params;
{A command line contains the executable name, e.g.
notepad.exe c:\somefile.txt
}

{From the Platform SDK:
The function can modify the contents of this string.
Therefore, this parameter cannot be a pointer to read-only memory
(such as a const variable or a literal string).
If this parameter is a constant string, the function may cause an
access violation.

ed: A function that tries to cause access violations. Nice.}

//An empty structure that the function wants
ZeroMemory(@startupInfo, SizeOf(startupInfo));
startupInfo.cb :=SizeOf(startupInfo);

//An empty structure that the function wants
ZeroMemory(@processInformation, SizeOf(processInformation));

Result := CreateProcessWithLogonW(
PWideChar(Username),
PWideChar(Domain),
PWideChar(Password),
LOGON_WITH_PROFILE, //dwLogonFlags: DWORD
PWideChar(Filename),
PWideChar(LocallyModifiableParams),
0, //dwCreationFlags: DWORD
nil, //lpEnvironment: Pointer
PWideChar(DefaultDir), //lpCurrentDirectory: PWideChar;
startupInfo,
processInformation);
if Result then
begin
{From the Platform SDK:
Handles in PROCESS_INFORMATION must be closed with the
CloseHandle
function when they are not needed.}

//We don't need either of the handles in ProcessInformation.
//Hopefully Microsoft won't add any more to the structure in
the future
CloseHandle(processInformation.hProcess);
CloseHandle(processInformation.hThread);
end;
end;


Hi,

Well, when using the API CreateProcessAsUser() does not succeed,
and the function GetLastError() returns the error code # 1314,
which is ERROR_PRIVILEGE_NOT_HELD, this is usually because the
users token does not have the needed access rights, which are the:

TOKEN_QUERY
TOKEN_DUPLICATE
TOKEN_ASSIGN_PRIMARY

Moreover, the process that calls the API CreateProcessAsUser(),
must have the following privileges:

SE_ASSIGNPRIMARYTOKEN_NAME
SE_INCREASE_QUOTA_NAME

So, another valid option is using the API CreateProcessWithLogonW(),
this would spawn your application in the correct security context,
and the function does exactly the same thing as LogonUserEx() and
CreateProcessAsUser(), and does not require any special privileges.
just make sure that the user account you specify is allowed to logIn
interactively to your machine, you check that in the domain/local
policies.

http://msdn2.microsoft.com/en-us/library/ms682429.aspx

http://msdn2.microsoft.com/en-us/library/ms682431.aspx

Kellie.
 
R

Roger Abell [MVP]

Ian Boyd said:
Is there a way to grant SeTcbPrivlege (Act as part of the operating
system) to a domain account without granting SeTcbPrivlege to the domain
account?

Did you proof read that questions? On the face of it, of course not.
If you were meaning grant on one member only, not at domain level (in
way that would be effective on multiple machines), yes that can be done.
However, SeTchPrivilege should not be given even on just that machine
as it does present a large security risk for anything running on the machine
that is limited only by how well use of the account is tightly controlled.
 
C

Chris Becke

Ian Boyd said:
Is there a way to grant SeTcbPrivlege (Act as part of the operating
system) to a domain account without granting SeTcbPrivlege to the domain
account? Obviously i'm not going to grant every user in the global
enterprise the ability to act as part of the operating system.

Yes. The general method to do this kind of elevation without granting non
admin users dangerous privlege's is to create a service process that uses an
account with the Privlege - the default SYSTEM account used by services
often sufices here. The application that needs to use the function that
needs the privlege would proxy the call to the service. The service can
decide wether the request is legitimate and perform it on behalf of the
user.

The idea being that the service's interface to non domain accounts is
strictly controlled to ensure that it cant be misused by users to do
anything other than the required elevation.
 
S

Stefan Kuhr

Hello Ian,

Ian said:
Is there a way to grant SeTcbPrivlege (Act as part of the operating system)
to a domain account without granting SeTcbPrivlege to the domain account?
Obviously i'm not going to grant every user in the global enterprise the
ability to act as part of the operating system.


You don't "grant every user in the global enterprise" a privilege, you
always grant a privilege to a user and a computer. This means that a
domain user can have the SE_TCB_NAME privilege on one computer in the
domain, but not on the other.
 
J

Jakob Bohm

Chris said:
Yes. The general method to do this kind of elevation without granting non
admin users dangerous privlege's is to create a service process that uses an
account with the Privlege - the default SYSTEM account used by services
often sufices here. The application that needs to use the function that
needs the privlege would proxy the call to the service. The service can
decide wether the request is legitimate and perform it on behalf of the
user.

The idea being that the service's interface to non domain accounts is
strictly controlled to ensure that it cant be misused by users to do
anything other than the required elevation.
Which is exactly what Microsoft has done for us with the "RunAs" service
included by default in Windows 2000 and later. The tightly controlled
API consists of CreateProcessWithLogonW and little else. For NT 4.0 you
can use the "su" service from the NT 4 Resource Kit Supplement 3 or
write your own. For NT 3.x you would need to write your own.

--
Jakob Bøhm, M.Sc.Eng. * (e-mail address removed) * direct tel:+45-45-90-25-33
Danware Data A/S * Bregnerodvej 127 * DK-3460 Birkerod * DENMARK
http://www.netop.com * tel:+45-45-90-25-25 * fax:+45-45-90-25-26
Information in this mail is hasty, not binding and may not be right.
Information in this posting may not be the official position of Danware
Data A/S, only the personal opinions of the author.
 
I

Ian Boyd

Is there a way to grant SeTcbPrivlege (Act as part of the operating
Did you proof read that questions? On the face of it, of course not.
If you were meaning grant on one member only, not at domain level (in
way that would be effective on multiple machines), yes that can be done.
However, SeTchPrivilege should not be given even on just that machine
as it does present a large security risk for anything running on the
machine
that is limited only by how well use of the account is tightly controlled.

Users are the ones who are running the application that needs to call
LogonUser.

It's not a good idea to give users the permission required to run the
software that will internally call LogonUser.

So i want to give all users the ability to run the software that calls
LogonUser, but not give them the privelages that are required to call
LogonUser.


In the end, i switched to CreateProcessWithLoginW. i thought it was only
available on XP and later, i didn't realize it was also available for 2000.

Which begs the question: What was the point of CreateProcessAsUser, since no
typical users can run it. Although, the answer also presents itself: There
was no point. That's why MS created CreateProcessWithLoginW, a version of
CreateProcessAsUser that users can run.
 
I

Ian Boyd

You don't "grant every user in the global enterprise" a privilege, you
always grant a privilege to a user and a computer.
This means that a domain user can have the SE_TCB_NAME privilege on one
computer in the domain, but not on the other.

The other what?

All user needs to be able to run some software that calls
CreateProcessAsUser internall, and they need to be able to run the software
wherever they login.


The answer is: don't try. Use CreateProcessWithLogonW
 
Joined
Aug 2, 2007
Messages
2
Reaction score
0
Here is some code i wrote a time ago

Private Const LOGON32_PROVIDER_DEFAULT As Integer = 0
Private Const LOGON32_LOGON_INTERACTIVE As Integer = 2
Private Const LOGON32_LOGON_NETWORK As Integer = 3
Private Const LOGON32_LOGON_BATCH As Integer = 4
Private Const LOGON32_LOGON_SERVICE As Integer = 5
Private Const LOGON32_LOGON_UNLOCK As Integer = 7
Private Const LOGON32_LOGON_NETWORK_CLEARTEXT As Integer = 8
Private Const LOGON32_LOGON_NEW_CREDENTIALS As Integer = 9

Private Shared ImpersonationContext As WindowsImpersonationContext

Declare Function LogonUserA Lib "advapi32.dll" ( _
ByVal lpszUsername As String, _
ByVal lpszDomain As String, _
ByVal lpszPassword As String, _
ByVal dwLogonType As Integer, _
ByVal dwLogonProvider As Integer, _
ByRef phToken As IntPtr) As Integer

Declare Auto Function DuplicateToken Lib "advapi32.dll" ( _
ByVal ExistingTokenHandle As IntPtr, _
ByVal ImpersonationLevel As Integer, _
ByRef DuplicateTokenHandle As IntPtr) As Integer
Declare Auto Function RevertToSelf Lib "advapi32.dll" () As Long
Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Long

Public Shared Function ImpersonateValidUser(ByVal strUserName As String, _
ByVal strDomain As String, ByVal strPassword As String) As Boolean
Dim token As IntPtr = IntPtr.Zero
Dim tokenDuplicate As IntPtr = IntPtr.Zero
Dim tempWindowsIdentity As WindowsIdentity

ImpersonateValidUser = False

If RevertToSelf() <> 0 Then
If LogonUserA(strUserName, strDomain, _
strPassword, _
LOGON32_LOGON_INTERACTIVE, _
LOGON32_PROVIDER_DEFAULT, token) <> 0 Then
If DuplicateToken(token, 2, tokenDuplicate) <> 0 Then
tempWindowsIdentity = New WindowsIdentity(tokenDuplicate)
ImpersonationContext = tempWindowsIdentity.Impersonate()

If Not (ImpersonationContext Is Nothing) Then
ImpersonateValidUser = True
End If
End If
End If
End If

If Not tokenDuplicate.Equals(IntPtr.Zero) Then
CloseHandle(tokenDuplicate)
End If
If Not token.Equals(IntPtr.Zero) Then
CloseHandle(token)
End If
End Function

Public Shared Sub UndoImpersonation()
ImpersonationContext.Undo()
End Sub


ImpersonateValidUser(Username, Domain, Password)
'// insert code here to run as different user
UndoImpersonation()


www.ict4you.com
 
S

Stefan Kuhr

Hello Ian

Ian said:
The other what?

The other computer.
All user needs to be able to run some software that calls
CreateProcessAsUser internall, and they need to be able to run the software
wherever they login.


The answer is: don't try. Use CreateProcessWithLogonW

If this solves your problem then use CreateProcessWithLogonW. However be
aware that some companies turn off the secondary logon service on a
domain wide scope.

CreateProcessAsUser can really only be used without problems if it is
called from a process running as SYSTEM.
 
I

Ian Boyd

CreateProcessAsUser can really only be used without problems if it is
called from a process running as SYSTEM.

So it's generally discouraged, and not the intended design pattern, for
applications to be using CreateProcessAsUser to create a process as a user.

And you're also saying that you can't rely on CreateProcessWithLogonW to be
available either.


Is there any preferred method for an application to create a process as a
user?
 
B

Ben Voigt [C++ MVP]

Ian Boyd said:
So it's generally discouraged, and not the intended design pattern, for
applications to be using CreateProcessAsUser to create a process as a
user.

And you're also saying that you can't rely on CreateProcessWithLogonW to
be available either.


Is there any preferred method for an application to create a process as a
user?

You can have your own service running as SYSTEM, and enforce more
restrictions than the "Secondary Logon" service on which user identities can
be used and which processes can be started, so it shouldn't be as big a
security concern for admins as "Secondary Logon" is. Then your application
can delegate process creation to that service, which in turn uses
CreateProcessAsUser .
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top