mirror of
https://github.com/reactos/reactos.git
synced 2025-01-11 16:51:06 +00:00
c424146e2c
svn path=/branches/cmake-bringup/; revision=48236
718 lines
19 KiB
C
718 lines
19 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS system libraries
|
|
* FILE: lib/advapi32/token/token.c
|
|
* PURPOSE: Token functions
|
|
* PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
|
|
* UPDATE HISTORY:
|
|
* Created 01/11/98
|
|
*/
|
|
|
|
#include <advapi32.h>
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(advapi);
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
AdjustTokenGroups(HANDLE TokenHandle,
|
|
BOOL ResetToDefault,
|
|
PTOKEN_GROUPS NewState,
|
|
DWORD BufferLength,
|
|
PTOKEN_GROUPS PreviousState,
|
|
PDWORD ReturnLength)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = NtAdjustGroupsToken(TokenHandle,
|
|
ResetToDefault,
|
|
NewState,
|
|
BufferLength,
|
|
PreviousState,
|
|
(PULONG)ReturnLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
AdjustTokenPrivileges(HANDLE TokenHandle,
|
|
BOOL DisableAllPrivileges,
|
|
PTOKEN_PRIVILEGES NewState,
|
|
DWORD BufferLength,
|
|
PTOKEN_PRIVILEGES PreviousState,
|
|
PDWORD ReturnLength)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = NtAdjustPrivilegesToken(TokenHandle,
|
|
DisableAllPrivileges,
|
|
NewState,
|
|
BufferLength,
|
|
PreviousState,
|
|
(PULONG)ReturnLength);
|
|
if (STATUS_NOT_ALL_ASSIGNED == Status)
|
|
{
|
|
SetLastError(ERROR_NOT_ALL_ASSIGNED);
|
|
return TRUE;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
/* AdjustTokenPrivileges is documented to do this */
|
|
SetLastError(ERROR_SUCCESS);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
GetTokenInformation(HANDLE TokenHandle,
|
|
TOKEN_INFORMATION_CLASS TokenInformationClass,
|
|
LPVOID TokenInformation,
|
|
DWORD TokenInformationLength,
|
|
PDWORD ReturnLength)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = NtQueryInformationToken(TokenHandle,
|
|
TokenInformationClass,
|
|
TokenInformation,
|
|
TokenInformationLength,
|
|
(PULONG)ReturnLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
SetTokenInformation(HANDLE TokenHandle,
|
|
TOKEN_INFORMATION_CLASS TokenInformationClass,
|
|
LPVOID TokenInformation,
|
|
DWORD TokenInformationLength)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = NtSetInformationToken(TokenHandle,
|
|
TokenInformationClass,
|
|
TokenInformation,
|
|
TokenInformationLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
AccessCheck(IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
IN HANDLE ClientToken,
|
|
IN DWORD DesiredAccess,
|
|
IN PGENERIC_MAPPING GenericMapping,
|
|
OUT PPRIVILEGE_SET PrivilegeSet OPTIONAL,
|
|
IN OUT LPDWORD PrivilegeSetLength,
|
|
OUT LPDWORD GrantedAccess,
|
|
OUT LPBOOL AccessStatus)
|
|
{
|
|
NTSTATUS Status;
|
|
NTSTATUS NtAccessStatus;
|
|
|
|
/* Do the access check */
|
|
Status = NtAccessCheck(pSecurityDescriptor,
|
|
ClientToken,
|
|
DesiredAccess,
|
|
GenericMapping,
|
|
PrivilegeSet,
|
|
(PULONG)PrivilegeSetLength,
|
|
(PACCESS_MASK)GrantedAccess,
|
|
&NtAccessStatus);
|
|
|
|
/* See if the access check operation succeeded */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Check failed */
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
/* Now check the access status */
|
|
if (!NT_SUCCESS(NtAccessStatus))
|
|
{
|
|
/* Access denied */
|
|
SetLastError(RtlNtStatusToDosError(NtAccessStatus));
|
|
*AccessStatus = FALSE;
|
|
}
|
|
else
|
|
{
|
|
/* Access granted */
|
|
*AccessStatus = TRUE;
|
|
}
|
|
|
|
/* Check succeeded */
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOL WINAPI AccessCheckByType(
|
|
PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
PSID PrincipalSelfSid,
|
|
HANDLE ClientToken,
|
|
DWORD DesiredAccess,
|
|
POBJECT_TYPE_LIST ObjectTypeList,
|
|
DWORD ObjectTypeListLength,
|
|
PGENERIC_MAPPING GenericMapping,
|
|
PPRIVILEGE_SET PrivilegeSet,
|
|
LPDWORD PrivilegeSetLength,
|
|
LPDWORD GrantedAccess,
|
|
LPBOOL AccessStatus)
|
|
{
|
|
FIXME("stub\n");
|
|
|
|
*AccessStatus = TRUE;
|
|
|
|
return !*AccessStatus;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
OpenProcessToken(HANDLE ProcessHandle,
|
|
DWORD DesiredAccess,
|
|
PHANDLE TokenHandle)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = NtOpenProcessToken(ProcessHandle,
|
|
DesiredAccess,
|
|
TokenHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
OpenThreadToken(HANDLE ThreadHandle,
|
|
DWORD DesiredAccess,
|
|
BOOL OpenAsSelf,
|
|
PHANDLE TokenHandle)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = NtOpenThreadToken(ThreadHandle,
|
|
DesiredAccess,
|
|
OpenAsSelf,
|
|
TokenHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
SetThreadToken(IN PHANDLE ThreadHandle OPTIONAL,
|
|
IN HANDLE TokenHandle)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE hThread;
|
|
|
|
hThread = (ThreadHandle != NULL) ? *ThreadHandle : NtCurrentThread();
|
|
|
|
Status = NtSetInformationThread(hThread,
|
|
ThreadImpersonationToken,
|
|
&TokenHandle,
|
|
sizeof(HANDLE));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
DuplicateTokenEx(IN HANDLE ExistingTokenHandle,
|
|
IN DWORD dwDesiredAccess,
|
|
IN LPSECURITY_ATTRIBUTES lpTokenAttributes OPTIONAL,
|
|
IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
|
|
IN TOKEN_TYPE TokenType,
|
|
OUT PHANDLE DuplicateTokenHandle)
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
NTSTATUS Status;
|
|
SECURITY_QUALITY_OF_SERVICE Sqos;
|
|
|
|
Sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
|
|
Sqos.ImpersonationLevel = ImpersonationLevel;
|
|
Sqos.ContextTrackingMode = 0;
|
|
Sqos.EffectiveOnly = FALSE;
|
|
|
|
if (lpTokenAttributes != NULL)
|
|
{
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
NULL,
|
|
lpTokenAttributes->bInheritHandle ? OBJ_INHERIT : 0,
|
|
NULL,
|
|
lpTokenAttributes->lpSecurityDescriptor);
|
|
}
|
|
else
|
|
{
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
ObjectAttributes.SecurityQualityOfService = &Sqos;
|
|
|
|
Status = NtDuplicateToken(ExistingTokenHandle,
|
|
dwDesiredAccess,
|
|
&ObjectAttributes,
|
|
FALSE,
|
|
TokenType,
|
|
DuplicateTokenHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
DuplicateToken(IN HANDLE ExistingTokenHandle,
|
|
IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
|
|
OUT PHANDLE DuplicateTokenHandle)
|
|
{
|
|
return DuplicateTokenEx(ExistingTokenHandle,
|
|
TOKEN_IMPERSONATE | TOKEN_QUERY,
|
|
NULL,
|
|
ImpersonationLevel,
|
|
TokenImpersonation,
|
|
DuplicateTokenHandle);
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
CheckTokenMembership(IN HANDLE ExistingTokenHandle,
|
|
IN PSID SidToCheck,
|
|
OUT PBOOL IsMember)
|
|
{
|
|
PISECURITY_DESCRIPTOR SecurityDescriptor = NULL;
|
|
ACCESS_MASK GrantedAccess;
|
|
struct
|
|
{
|
|
PRIVILEGE_SET PrivilegeSet;
|
|
LUID_AND_ATTRIBUTES Privileges[4];
|
|
} PrivBuffer;
|
|
ULONG PrivBufferSize = sizeof(PrivBuffer);
|
|
GENERIC_MAPPING GenericMapping =
|
|
{
|
|
STANDARD_RIGHTS_READ,
|
|
STANDARD_RIGHTS_WRITE,
|
|
STANDARD_RIGHTS_EXECUTE,
|
|
STANDARD_RIGHTS_ALL
|
|
};
|
|
PACL Dacl;
|
|
ULONG SidLen;
|
|
HANDLE hToken = NULL;
|
|
NTSTATUS Status, AccessStatus;
|
|
|
|
/* doesn't return gracefully if IsMember is NULL! */
|
|
*IsMember = FALSE;
|
|
|
|
SidLen = RtlLengthSid(SidToCheck);
|
|
|
|
if (ExistingTokenHandle == NULL)
|
|
{
|
|
Status = NtOpenThreadToken(NtCurrentThread(),
|
|
TOKEN_QUERY,
|
|
FALSE,
|
|
&hToken);
|
|
|
|
if (Status == STATUS_NO_TOKEN)
|
|
{
|
|
/* we're not impersonating, open the primary token */
|
|
Status = NtOpenProcessToken(NtCurrentProcess(),
|
|
TOKEN_QUERY | TOKEN_DUPLICATE,
|
|
&hToken);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
HANDLE hNewToken = FALSE;
|
|
BOOL DupRet;
|
|
|
|
/* duplicate the primary token to create an impersonation token */
|
|
DupRet = DuplicateTokenEx(hToken,
|
|
TOKEN_QUERY | TOKEN_IMPERSONATE,
|
|
NULL,
|
|
SecurityImpersonation,
|
|
TokenImpersonation,
|
|
&hNewToken);
|
|
|
|
NtClose(hToken);
|
|
|
|
if (!DupRet)
|
|
{
|
|
WARN("Failed to duplicate the primary token!\n");
|
|
return FALSE;
|
|
}
|
|
|
|
hToken = hNewToken;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hToken = ExistingTokenHandle;
|
|
}
|
|
|
|
/* create a security descriptor */
|
|
SecurityDescriptor = RtlAllocateHeap(RtlGetProcessHeap(),
|
|
0,
|
|
sizeof(SECURITY_DESCRIPTOR) +
|
|
sizeof(ACL) + SidLen +
|
|
sizeof(ACCESS_ALLOWED_ACE));
|
|
if (SecurityDescriptor == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlCreateSecurityDescriptor(SecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* set the owner and group */
|
|
Status = RtlSetOwnerSecurityDescriptor(SecurityDescriptor,
|
|
SidToCheck,
|
|
FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlSetGroupSecurityDescriptor(SecurityDescriptor,
|
|
SidToCheck,
|
|
FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* create the DACL */
|
|
Dacl = (PACL)(SecurityDescriptor + 1);
|
|
Status = RtlCreateAcl(Dacl,
|
|
sizeof(ACL) + SidLen + sizeof(ACCESS_ALLOWED_ACE),
|
|
ACL_REVISION);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = RtlAddAccessAllowedAce(Dacl,
|
|
ACL_REVISION,
|
|
0x1,
|
|
SidToCheck);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* assign the DACL to the security descriptor */
|
|
Status = RtlSetDaclSecurityDescriptor(SecurityDescriptor,
|
|
TRUE,
|
|
Dacl,
|
|
FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* it's time to perform the access check. Just use _some_ desired access right
|
|
(same as for the ACE) and see if we're getting it granted. This indicates
|
|
our SID is a member of the token. We however can't use a generic access
|
|
right as those aren't mapped and return an error (STATUS_GENERIC_NOT_MAPPED). */
|
|
Status = NtAccessCheck(SecurityDescriptor,
|
|
hToken,
|
|
0x1,
|
|
&GenericMapping,
|
|
&PrivBuffer.PrivilegeSet,
|
|
&PrivBufferSize,
|
|
&GrantedAccess,
|
|
&AccessStatus);
|
|
if (NT_SUCCESS(Status) && NT_SUCCESS(AccessStatus) && (GrantedAccess == 0x1))
|
|
{
|
|
*IsMember = TRUE;
|
|
}
|
|
|
|
Cleanup:
|
|
if (hToken != NULL && hToken != ExistingTokenHandle)
|
|
{
|
|
NtClose(hToken);
|
|
}
|
|
|
|
if (SecurityDescriptor != NULL)
|
|
{
|
|
RtlFreeHeap(RtlGetProcessHeap(),
|
|
0,
|
|
SecurityDescriptor);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL WINAPI
|
|
IsTokenRestricted(HANDLE TokenHandle)
|
|
{
|
|
ULONG RetLength;
|
|
PTOKEN_GROUPS lpGroups;
|
|
NTSTATUS Status;
|
|
BOOL Ret = FALSE;
|
|
|
|
/* determine the required buffer size and allocate enough memory to read the
|
|
list of restricted SIDs */
|
|
Status = NtQueryInformationToken(TokenHandle,
|
|
TokenRestrictedSids,
|
|
NULL,
|
|
0,
|
|
&RetLength);
|
|
if (Status != STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
AllocAndReadRestrictedSids:
|
|
lpGroups = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(),
|
|
0,
|
|
RetLength);
|
|
if (lpGroups == NULL)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
/* actually read the list of the restricted SIDs */
|
|
Status = NtQueryInformationToken(TokenHandle,
|
|
TokenRestrictedSids,
|
|
lpGroups,
|
|
RetLength,
|
|
&RetLength);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Ret = (lpGroups->GroupCount != 0);
|
|
}
|
|
else if (Status == STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
/* looks like the token was modified in the meanwhile, let's just try again */
|
|
HeapFree(GetProcessHeap(),
|
|
0,
|
|
lpGroups);
|
|
|
|
goto AllocAndReadRestrictedSids;
|
|
}
|
|
else
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
}
|
|
|
|
/* free allocated memory */
|
|
HeapFree(GetProcessHeap(),
|
|
0,
|
|
lpGroups);
|
|
|
|
return Ret;
|
|
}
|
|
|
|
|
|
BOOL WINAPI
|
|
CreateRestrictedToken(HANDLE TokenHandle,
|
|
DWORD Flags,
|
|
DWORD DisableSidCount,
|
|
PSID_AND_ATTRIBUTES pSidAndAttributes,
|
|
DWORD DeletePrivilegeCount,
|
|
PLUID_AND_ATTRIBUTES pLUIDAndAttributes,
|
|
DWORD RestrictedSidCount,
|
|
PSID_AND_ATTRIBUTES pSIDAndAttributes,
|
|
PHANDLE NewTokenHandle)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
PSID
|
|
WINAPI
|
|
GetSiteSidFromToken(IN HANDLE TokenHandle)
|
|
{
|
|
PTOKEN_GROUPS RestrictedSids;
|
|
ULONG RetLen;
|
|
UINT i;
|
|
NTSTATUS Status;
|
|
PSID PSiteSid = NULL;
|
|
SID_IDENTIFIER_AUTHORITY InternetSiteAuthority = {SECURITY_INTERNETSITE_AUTHORITY};
|
|
|
|
Status = NtQueryInformationToken(TokenHandle,
|
|
TokenRestrictedSids,
|
|
NULL,
|
|
0,
|
|
&RetLen);
|
|
if (Status != STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return NULL;
|
|
}
|
|
|
|
RestrictedSids = (PTOKEN_GROUPS)RtlAllocateHeap(RtlGetProcessHeap(),
|
|
0,
|
|
RetLen);
|
|
if (RestrictedSids == NULL)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
Status = NtQueryInformationToken(TokenHandle,
|
|
TokenRestrictedSids,
|
|
RestrictedSids,
|
|
RetLen,
|
|
&RetLen);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
for (i = 0; i < RestrictedSids->GroupCount; i++)
|
|
{
|
|
SID* RSSid = RestrictedSids->Groups[i].Sid;
|
|
|
|
if (RtlCompareMemory(&(RSSid->IdentifierAuthority),
|
|
&InternetSiteAuthority,
|
|
sizeof(SID_IDENTIFIER_AUTHORITY)) ==
|
|
sizeof(SID_IDENTIFIER_AUTHORITY))
|
|
{
|
|
PSiteSid = RtlAllocateHeap(RtlGetProcessHeap(),
|
|
0,
|
|
RtlLengthSid((RestrictedSids->
|
|
Groups[i]).Sid));
|
|
if (PSiteSid == NULL)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
}
|
|
else
|
|
{
|
|
RtlCopySid(RtlLengthSid(RestrictedSids->Groups[i].Sid),
|
|
PSiteSid,
|
|
RestrictedSids->Groups[i].Sid);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
}
|
|
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, RestrictedSids);
|
|
return PSiteSid;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
CreateProcessWithTokenW(IN HANDLE hToken,
|
|
IN DWORD dwLogonFlags,
|
|
IN LPCWSTR lpApplicationName OPTIONAL,
|
|
IN OUT LPWSTR lpCommandLine OPTIONAL,
|
|
IN DWORD dwCreationFlags,
|
|
IN LPVOID lpEnvironment OPTIONAL,
|
|
IN LPCWSTR lpCurrentDirectory OPTIONAL,
|
|
IN LPSTARTUPINFOW lpStartupInfo,
|
|
OUT LPPROCESS_INFORMATION lpProcessInfo)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|