Related
I need to get system folders for current user via SHGetFolderPath, but in the installer application the system elevates this to the admin account and it returns admin account folders instead. How to get the security token for the current user? I tried this:
HANDLE token = NULL;
PWTS_SESSION_INFO sessions;
DWORD cnt = 0;
WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &sessions, &cnt);
for (DWORD i=0; i<cnt; i++)
{
if (WTSActive == sessions->State)
{
WTSQueryUserToken(sessions->SessionId, &token);
break;
};
sessions++;
};
But it doesn't really work. Any ideas?
possible get shell process id (explorer), take token from it and use this token in query.
if (HWND hwnd = GetShellWindow())
{
ULONG dwProcessId;
if (GetWindowThreadProcessId(hwnd, &dwProcessId))
{
HANDLE hToken;
if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwProcessId))
{
BOOL b = OpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_IMPERSONATE, &hToken);
CloseHandle(hProcess);
if (b)
{
if (0 <= SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, hToken, &pszPath))
{
DbgPrint("<%S>\n", pszPath);
CoTaskMemFree(pszPath);
}
CloseHandle(hToken);
}
}
}
}
We are writing a run-only remote desktop application, that uses SendInput for keyboard (& mouse) interaction. However it cannot interact with UAC prompts.
What permissions/rights does our application require for this?
Background info: The application is spawned by another process duplicating winlogon.exe's Access Token. This enables to run under SYSTEM account with System Integrity Level, is attached to the Physical Console Session and has the same SE privileges as winlogon.exe (https://learn.microsoft.com/en-us/windows/desktop/secauthz/privilege-constants), although not all of them are enabled.
struct MY_TOKEN_PRIVILEGES {
DWORD PrivilegeCount;
LUID_AND_ATTRIBUTES Privileges[2];
};
int RunUnderWinLogon(LPCWSTR executableWithSendInput)
{
DWORD physicalConsoleSessionId = WTSGetActiveConsoleSessionId();
auto winlogonPid = GetWinLogonPid(); // external function
if (!winlogonPid)
{
std::cout << "ERROR getting winlogon pid" << std::endl;
return 0;
}
HANDLE hWinlogonToken, hProcess;
hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, winlogonPid);
if (!::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY
| TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID
| TOKEN_READ | TOKEN_WRITE, &hWinlogonToken))
{
printf("Process token open Error: %u\n", GetLastError());
}
// Is security descriptor needed for SendInput?
PSECURITY_DESCRIPTOR pSD = NULL;
SECURITY_ATTRIBUTES saToken;
ZeroMemory(&saToken, sizeof(SECURITY_ATTRIBUTES));
saToken.nLength = sizeof (SECURITY_ATTRIBUTES);
saToken.lpSecurityDescriptor = pSD;
saToken.bInheritHandle = FALSE;
HANDLE hWinlogonTokenDup;
if (!DuplicateTokenEx(hWinlogonToken, TOKEN_ALL_ACCESS, &saToken, SecurityImpersonation, TokenPrimary, &hWinlogonTokenDup))
{
printf("DuplicateTokenEx Error: %u\n", GetLastError());
}
if (!SetTokenInformation(hWinlogonTokenDup, TokenSessionId, (void*)physicalConsoleSessionId, sizeof(DWORD)))
{
printf("SetTokenInformation Error: %u\n", GetLastError());
}
//Adjust Token privilege
LUID luidSeDebugName;
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luidSeDebugName))
{
printf("Lookup Privilege value Error: %u\n", GetLastError());
}
LUID luidSeTcbName;
if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &luidSeTcbName))
{
printf("Lookup Privilege value Error: %u\n", GetLastError());
}
MY_TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 2;
tp.Privileges[0].Luid = luidSeDebugName;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
tp.Privileges[1].Luid = luidSeTcbName;
tp.Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hWinlogonTokenDup, FALSE, (PTOKEN_PRIVILEGES)&tp, /*BufferLength*/0, /*PreviousState*/(PTOKEN_PRIVILEGES)NULL, NULL))
{
printf("Adjust Privilege value Error: %u\n", GetLastError());
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
printf("Token does not have the privilege\n");
}
DWORD creationFlags;
creationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
LPVOID pEnv = NULL;
if (CreateEnvironmentBlock(&pEnv, hWinlogonTokenDup, TRUE))
{
std::cout << "CreateEnvironmentBlock() success" << std::endl;
creationFlags |= CREATE_UNICODE_ENVIRONMENT;
}
SECURITY_ATTRIBUTES saProcess, saThread;
ZeroMemory(&saProcess, sizeof(SECURITY_ATTRIBUTES));
ZeroMemory(&saThread, sizeof(SECURITY_ATTRIBUTES));
saProcess.nLength = sizeof (SECURITY_ATTRIBUTES);
saProcess.lpSecurityDescriptor = pSD;
saProcess.bInheritHandle = FALSE;
saThread.nLength = sizeof (SECURITY_ATTRIBUTES);
saThread.lpSecurityDescriptor = pSD;
saThread.bInheritHandle = FALSE;
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = (LPWSTR)L"winsta0\\default";
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
BOOL bResult = CreateProcessAsUser(
hWinlogonTokenDup, // client's access token
executableWithSendInput, // file using SendInput
NULL, // command line
&saProcess, // pointer to process SECURITY_ATTRIBUTES
&saThread, // pointer to thread SECURITY_ATTRIBUTES
FALSE, // handles are not inheritable
creationFlags, // creation flags
pEnv, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&pi // receives information about new process
);
}
SendInput, like SendMessage and PostMessage is limited to work between processes in the same login session and within the same desktop as the target process. The UAC prompt is shown in the Winlogon's Secure Desktop (winsta0\Winlogon) so you need poll the current desktop periodically with OpenInputDesktop() then use SetThreadDesktop() to enable the current thread to send messages to the user's desktop / secure desktop, whichever active is.
In case of UAC, you need to run your process under the System Account, to comply with the UIPI Integrity Level check, as you already did.
See also: How to switch a process between default desktop and Winlogon desktop?
It is possible to authorize your application to be able to do these UIAutomation/screen reader tasks.
Create an entry in your assembly manifest that includes:
uiAccess="true"
Then you have to be digitally sign with a valid digital certificate.
And you have to be installed in Program Files.
Being able to automate the UAC dialog is serious business; and you don't get to screw around with that willy-nilly.
Bonus Reading
https://techcommunity.microsoft.com/t5/windows-blog-archive/using-the-uiaccess-attribute-of-requestedexecutionlevel-to/ba-p/228641
When installing SQL Server 2008, if this privilege is not enabled for the user doing the install, the install fails un-gracefully. So in my app, before installing SQL Server (using its silent install), I'd like to detect whether or not the current running user has the "Debug Programs" privilege set (i.e., SeDebugPrivilege, SE_DEBUG_NAME...)
I don't want to know if the current process has it set (because, apparently, most times it does not, even if the privilege is enabled on the system). I originally thought that the "PrivilegeCheck" API would work, but it does not. If you run this code under the VS debugger, then it tells you the privilege is enabled. If you run it from a command line, it tells you the privilege is disabled. How should I correct this program to actually be able to check whether or not the privilege is available?
HANDLE hToken;
// Get the calling thread's access token.
if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
{
if (GetLastError() != ERROR_NO_TOKEN)
{
printf("CAN'T GET THREAD TOKEN!!!\n");
return -1;
}
// Retry against process token if no thread token exists.
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
{
printf("CAN'T GET PROCESS TOKEN!!!\n");
return -1;
}
}
//Find the LUID for the debug privilege token
LUID luidDebugPrivilege;
if ( !LookupPrivilegeValue(
NULL, // lookup privilege on local system
"SeDebugPrivilege", // privilege to lookup
&luidDebugPrivilege ) ) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError() );
return -1;
}
PRIVILEGE_SET privs;
privs.PrivilegeCount = 1;
privs.Control = PRIVILEGE_SET_ALL_NECESSARY;
privs.Privilege[0].Luid = luidDebugPrivilege;
privs.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
BOOL bResult;
::PrivilegeCheck(hToken, &privs, &bResult);
if(bResult)
{
printf("DEBUG ENABLED!\n");
}
else
{
printf("DEBUG NOT ENABLED!\n");
}
OK, we figured this out after posting the original question. What we actually need to do is attempt to set the "debug programs" privilege for the current process. If we can enable that privilege, then that means the current logged-in user has that privilege enabled for them in the local security policy editor (gpedit.msc on XP...)
See below for example code, in case anyone else needs to solve this problem! The important pieces are:
Use LookupPrivilegeValue() to find the LUID for SeDebugPrivilege. (All the APIs for this stuff need LUIDs...)
Use GetTokenInformation() to find out what privileges are enabled on this process already. If the process has the privilege enabled already, that means that the process is most likely being run under a debugger, and that the current logged-in user does have the privilege enabled.
If the process doesn't have the privilege set, use the AdjustTokenPrivileges() to attempt to set the privilege. This is in our method AttemptToAddDebugPrivilegeToProcess() below; we return true if the privilege can be set (meaning the current logged-in user has the "debug programs" privilege enabled) or false if it can't.
#include "stdafx.h"
#include <strsafe.h>
void ShowLastError(LPTSTR lpszFunction) {
// Retrieve the system error message for the last-error code
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
// Display the error message and exit the process
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
printf((LPTSTR)lpDisplayBuf);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}
bool LuidsMatch(LUID l1, LUID l2)
{
return l1.LowPart == l2.LowPart && l1.HighPart == l2.HighPart; }
bool AttemptToAddDebugPrivilegeToProcess(HANDLE hToken) {
//Find the LUID for the debug privilege token
LUID luidDebugPrivilege;
if ( !LookupPrivilegeValue(
NULL, // lookup privilege on local system
"SeDebugPrivilege", // privilege to lookup
&luidDebugPrivilege ) ) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError() );
return false;
}
TOKEN_PRIVILEGES newState;
newState.PrivilegeCount = 1;
newState.Privileges[0].Luid = luidDebugPrivilege;
newState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(AdjustTokenPrivileges(
hToken,
FALSE,
&newState,
sizeof(newState),
NULL, //&previousState,
0))
{
if(GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
printf("Couldn't set debug!!!");
return false;
}
//*************************************************************
//IF YOU MADE IT HERE, THE USER HAS THE DEBUG PROGRAMS PRIVILEGE
//*************************************************************
printf("DEBUG OK!!!");
return true;
}
printf("AdjustTokenPrivileges returned false!!!");
ShowLastError("AdjustTokenPrivileges");
return false;
}
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hToken;
// Get the calling thread's access token.
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, TRUE, &hToken))
{
if (GetLastError() != ERROR_NO_TOKEN)
{
printf("CAN'T GET THREAD TOKEN!!!\n");
return -1;
}
// Retry against process token if no thread token exists.
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken))
{
printf("CAN'T GET PROCESS TOKEN!!!\n");
return -1;
}
}
//Find the LUID for the debug privilege token
LUID luidDebugPrivilege;
if ( !LookupPrivilegeValue(
NULL, // lookup privilege on local system
"SeDebugPrivilege", // privilege to lookup
&luidDebugPrivilege ) ) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError() );
return -1;
}
//Find if the "debug programs" privilege is already assigned to this process
DWORD dwReturnedDataSize;
GetTokenInformation(
hToken,
TokenPrivileges,
NULL,
0,
&dwReturnedDataSize);
BYTE* pData = new BYTE[dwReturnedDataSize];
GetTokenInformation(
hToken,
TokenPrivileges,
pData,
dwReturnedDataSize,
&dwReturnedDataSize);
TOKEN_PRIVILEGES* pPrivileges = (TOKEN_PRIVILEGES*)pData;
bool bFound = false;
for(unsigned int count = 0; count PrivilegeCount; count++)
{
LUID_AND_ATTRIBUTES& luidAndAttrs = pPrivileges->Privileges[count];
if(LuidsMatch(luidAndAttrs.Luid, luidDebugPrivilege))
{
bFound = true;
if((luidAndAttrs.Attributes & SE_PRIVILEGE_ENABLED) == SE_PRIVILEGE_ENABLED)
{
//**************************************************************
//IF YOU MADE IT HERE, THE USER HAS THE DEBUG PROGRAMS PRIVILEGE
//**************************************************************
}
else
{
printf("THIS PROCESS DOES NOT HAVE THE DEBUG PROGRAMS PRIVILEGE ENABLED\n"); AttemptToAddDebugPrivilegeToProcess(hToken);
}
}
}
if(!bFound)
{
printf("THIS PROCESS DOES NOT HAVE THE DEBUG PROGRAMS PRIVILEGE ENABLED\n");
AttemptToAddDebugPrivilegeToProcess(hToken);
}
return 0;
}
The function GetTokenInformation can be used to retrieve the list of privileges for the process. PrivilegeCheck checks if a privilege is enabled or disabled, and privileges not held by the user will always be disabled. Privileges held by the user may or may not be disabled (some are disabled by default)
From your question, I think what you really want is to check is if the user is an administrator.
If I understand you correct you can use LsaEnumerateAccountRights to get the list of privileges which the user has.
I want create a process under another user. So I use LogonUser and CreateProcessAsUser. But my problem is, that CreatePtocessAsUser always returns the errorcode 1314, which means "A required privilige is not held by the client". So my question is, what I am doing wrong? Or how can i give the priviliges to the handle? (I think the handle should have the privileges, or I am wrong?) Sorry for my english mistakes, but my english knowledge isn't the best :)
Plesase help if anyone knows how to correct my application.
This a part of my code.
STARTUPINFO StartInfo;
PROCESS_INFORMATION ProcInfo;
TOKEN_PRIVILEGES tp;
memset(&ProcInfo, 0, sizeof(ProcInfo));
memset(&StartInfo, 0 , sizeof(StartInfo));
StartInfo.cb = sizeof(StartInfo);
HANDLE handle = NULL;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ALL_ACCESS, &handle)) printf("\nOpenProcessError");
if (!LookupPrivilegeValue(NULL,SE_TCB_NAME,
//SE_TCB_NAME,
&tp.Privileges[0].Luid)) {
printf("\nLookupPriv error");
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes =
SE_PRIVILEGE_ENABLED;//SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(handle, FALSE, &tp, 0, NULL, 0)) {
printf("\nAdjustToken error");
}
i = LogonUser(user, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &handle);
printf("\nLogonUser return : %d",i);
i = GetLastError();
printf("\nLogonUser getlast : %d",i);
if (! ImpersonateLoggedOnUser(handle) ) printf("\nImpLoggedOnUser!");
i = CreateProcessAsUser(handle, "c:\\windows\\system32\\notepad.exe",NULL, NULL, NULL, true,
CREATE_UNICODE_ENVIRONMENT |NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE, NULL, NULL,
&StartInfo, &ProcInfo);
printf("\nCreateProcessAsUser return : %d",i);
i = GetLastError();
printf("\nCreateProcessAsUser getlast : %d",i);
CloseHandle(handle);
CloseHandle(ProcInfo.hProcess);
CloseHandle(ProcInfo.hThread);
Thanks in advance!
The local account that is running your app must have these privileges enabled in the Local Security Policy:
Act as part of the operating system
Create a token object
Log on as a batch job
Edit: Please see Patel's answer below. The correct privilege in this case should be:
"Replace a process level token"
After looking for answer for hours, I finally found it in following link from MSDN. Hope it may help someone in future.
https://social.msdn.microsoft.com/Forums/vstudio/en-US/c905c900-cae1-4081-b0c9-00f10238e7ad/createprocessasuser-failed?forum=clr
"To resolve this problem, you'll need to elevate the rights of the account calling CreateProcessAsUser with the "Replace a process level token" right. To do so, open the Control Panel / Administrative Tools / Local Security Policy and add the user account to the "Replace a process level token" right. (You may have to logout or even reboot to have this change take effect.)"
Your code adds the SE_TCB_NAME privilege to your token.
MSDN says "Typically, the process that calls the CreateProcessAsUser function must have the SE_ASSIGNPRIMARYTOKEN_NAME and SE_INCREASE_QUOTA_NAME privileges."
I checked the links, and it worked good.
Check this
void main()
{
DWORD dwSessionId;
HANDLE hToken = NULL;
TOKEN_PRIVILEGES tp;
PROCESS_INFORMATION pi;
STARTUPINFOW si;
// Initialize structures.
ZeroMemory(&tp, sizeof(tp));
ZeroMemory(&pi, sizeof(pi));
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
LPTSTR lpszUsername = "user\0";
LPTSTR lpszDomain = ".";//"bgt\0";
LPTSTR lpszPassword = "password\0";
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY
| TOKEN_ADJUST_PRIVILEGES , &hToken)) {
MyError();
}
// Look up the LUID for the TCB Name privilege.
if (!LookupPrivilegeValue(NULL,SE_TCB_NAME, //SE_SHUTDOWN_NAME ,
//SE_TCB_NAME,
&tp.Privileges[0].Luid)) {
MyError();
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes =
SE_PRIVILEGE_ENABLED;//SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, 0)) {
MyError();
}
if(LogonUser(lpszUsername,lpszDomain,lpszPassword,
LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT,&hToken) == 0)
{
MyError();
}
else
{
STARTUPINFO sInfo;
PROCESS_INFORMATION ProcessInfo;
memset(&sInfo,0,sizeof(STARTUPINFO));
sInfo.cb = sizeof(STARTUPINFO);
sInfo.dwX = CW_USEDEFAULT;
sInfo.dwY = CW_USEDEFAULT;
sInfo.dwXSize = CW_USEDEFAULT;
sInfo.dwYSize = CW_USEDEFAULT;
bool bRet = CreateProcessAsUser(hToken,
"c:\\windows\\system32\\notepad.exe",
NULL,
NULL,
NULL,
TRUE,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&sInfo,
&ProcessInfo);
if(bRet == 0)
MyError();
}
I have some code, with which I'm trying to get the current session user token:
#include <Wtsapi32.h>
DWORD activeSessionId = WTSGetActiveConsoleSessionId();
HANDLE currentToken;
BOOL queryRet = WTSQueryUserToken(activeSessionId, ¤tToken);
if (!queryRet) {
DWORD err = GetLastError();
return 0;
}
Value of err is 1314.
Update 1
No luck so far, tried to grant the current process SE_TCB_NAME - but still get same error from WTSQueryUserToken (1314).
HANDLE process = GetCurrentProcess();
HANDLE processToken;
BOOL openTokenRet = OpenProcessToken(
process, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &processToken);
if (!openTokenRet)
{
DWORD err = GetLastError();
return 0;
}
TOKEN_PRIVILEGES tokenPrivs;
BOOL lookupRet = LookupPrivilegeValue(
NULL, SE_TCB_NAME, &tokenPrivs.Privileges[0].Luid);
if (!lookupRet)
{
DWORD err = GetLastError();
return 0;
}
tokenPrivs.PrivilegeCount = 1;
tokenPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
BOOL adjustRet = AdjustTokenPrivileges(
processToken, FALSE, &tokenPrivs, 0, (PTOKEN_PRIVILEGES)NULL, 0);
if (!adjustRet)
{
DWORD err = GetLastError();
return 0;
}
// get the user in the active session
HANDLE currentToken;
BOOL queryRet = WTSQueryUserToken(activeSessionId, ¤tToken);
if (!queryRet) {
DWORD err = GetLastError();
return 0;
}
Update 2:
Added some more debug info, but prevState.PrivilegeCount is 0...
TOKEN_PRIVILEGES prevState;
DWORD prevStateLen = 0;
BOOL adjustRet = AdjustTokenPrivileges(
processToken, FALSE, &tokenPrivs,
sizeof(TOKEN_PRIVILEGES), &prevState, &prevStateLen);
DWORD adjustErr = GetLastError();
if (!adjustRet)
{
return 0;
}
Solution:
Looks like WTSQueryUserToken can only be used when running as LocalSystem, which means I'll have to run as a service and debug from there... Doh!
Error 1314 is ERROR_PRIVILEGE_NOT_HELD. You need SE_TCB_NAME privilege to call WTSQueryUserToken.
This privilege is typically held only by code running as Local System. If this privilege is present in your token, but disabled, you can use AdjustTokenPrivileges to enable it. Since SE_TCB_NAME is a potentially very dangerous privilege to have, you should disable it again immediately after you use it. An easy way to see if you have this privilege is with Process Explorer in the Security table of the process properties window.
Per Update 1 - is AdjustTokenPrivileges return success, but GetLastError() is set to ERROR_NOT_ALL_ASSIGNED? MSDN indicates it could return this if privileges weren't enabled. Can you verify that your process does have SE_TCB_NAME privilege, but it is disabled? What account is your process running as?