Related
I have an elevated process that has been started after the user has answered "Yes" in the UAC dialog.
The process is started fine, everything works as expected.
Now I need to "unelevate" that process at some point, in other words the process should become not elevated exactly as if it had been launched normally by the user.
Sample scenario
User A is logged on
User A launches process P1 which will be elevated via UAC
Elevated process P1 lauchches process P2 and P2 should'nt be elevated and should run again under user A.
Is there a way to do this?
the elevated process have linked token - it refers to non elevated user session. we can use this linked token in 2 ways:
first way:
get it as TokenPrimary (for this we need have SE_TCB_PRIVILEGE
when we query this token)
call CreateProcessAsUser with this token. for this we need also
SE_ASSIGNPRIMARYTOKEN_PRIVILEGE and SE_INCREASE_QUOTA_PRIVILEGE
for get all this privileges - enumerate processes, query it tokens,
and if process token have all this 3 privileges - impersonate with
it, before call CreateProcessAsUser. because elevated token have
SE_DEBUG_PRIVILEGE the task is possible
second way:
query the logon session id from linked token (AuthenticationId
from TOKEN_STATISTICS)
found process with the same AuthenticationId in process token.
use this process as parent process by help
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
implementation for way 1:
static volatile UCHAR guz;
ULONG RunNonElevated(HANDLE hToken, HANDLE hMyToken, PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
ULONG err;
PVOID stack = alloca(guz);
ULONG cb = 0, rcb = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[SE_MAX_WELL_KNOWN_PRIVILEGE]);
union {
PVOID buf;
PTOKEN_PRIVILEGES ptp;
};
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (GetTokenInformation(hToken, TokenPrivileges, buf, cb, &rcb))
{
if (ULONG PrivilegeCount = ptp->PrivilegeCount)
{
int n = 3;
BOOL fAdjust = FALSE;
PLUID_AND_ATTRIBUTES Privileges = ptp->Privileges;
do
{
switch (Privileges->Luid.LowPart)
{
case SE_ASSIGNPRIMARYTOKEN_PRIVILEGE:
case SE_INCREASE_QUOTA_PRIVILEGE:
case SE_TCB_PRIVILEGE:
if (!(Privileges->Attributes & SE_PRIVILEGE_ENABLED))
{
Privileges->Attributes |= SE_PRIVILEGE_ENABLED;
fAdjust = TRUE;
}
if (!--n)
{
err = NOERROR;
if (DuplicateTokenEx(hToken,
TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE,
0, SecurityImpersonation, TokenImpersonation,
&hToken))
{
if (fAdjust)
{
AdjustTokenPrivileges(hToken, FALSE, ptp, rcb, NULL, NULL);
err = GetLastError();
}
if (err == NOERROR)
{
if (SetThreadToken(0, hToken))
{
TOKEN_LINKED_TOKEN tlt;
if (GetTokenInformation(hMyToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb))
{
STARTUPINFO si = { sizeof (si) };
PROCESS_INFORMATION pi;
if (!CreateProcessAsUserW(tlt.LinkedToken, lpApplicationName, lpCommandLine,
NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
err = GetLastError();
}
CloseHandle(tlt.LinkedToken);
if (err == NOERROR)
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
}
else
{
err = GetLastError();
}
SetThreadToken(0, 0);
}
else
{
err = GetLastError();
}
}
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
return err;
}
}
} while (Privileges++, --PrivilegeCount);
}
return ERROR_NOT_FOUND;
}
} while ((err = GetLastError()) == ERROR_INSUFFICIENT_BUFFER);
return err;
}
ULONG RunNonElevated(HANDLE hMyToken, PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
static TOKEN_PRIVILEGES tp = {
1, { { { SE_DEBUG_PRIVILEGE } , SE_PRIVILEGE_ENABLED } }
};
AdjustTokenPrivileges(hMyToken, FALSE, &tp, sizeof(tp), NULL, NULL);
ULONG err = NOERROR;
// much more effective of course use NtQuerySystemInformation(SystemProcessesAndThreadsInformation) here
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0), hToken;
if (hSnapshot != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32W pe = { sizeof(pe) };
if (Process32FirstW(hSnapshot, &pe))
{
err = ERROR_NOT_FOUND;
do
{
if (pe.th32ProcessID && pe.th32ParentProcessID)
{
if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID))
{
if (OpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE, &hToken))
{
err = RunNonElevated(hToken, hMyToken, lpApplicationName, lpCommandLine);
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
CloseHandle(hProcess);
}
else
{
err = GetLastError();
}
}
} while (err && Process32NextW(hSnapshot, &pe));
}
else
{
err = GetLastError();
}
CloseHandle(hSnapshot);
}
return err;
}
ULONG RunNonElevated(PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
HANDLE hToken;
ULONG err = NOERROR;
if (OpenProcessToken(NtCurrentProcess(), TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_ELEVATION_TYPE tet;
ULONG rcb;
if (GetTokenInformation(hToken, ::TokenElevationType, &tet, sizeof(tet), &rcb))
{
if (tet == TokenElevationTypeFull)
{
RunNonElevated(hToken, lpApplicationName, lpCommandLine);
}
else
{
err = ERROR_ALREADY_ASSIGNED;
}
}
else
{
err = GetLastError();
}
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
return err;
}
implementation for way 2:
ULONG CreateProcessEx(HANDLE hProcess,
PCWSTR lpApplicationName,
PWSTR lpCommandLine)
{
SIZE_T Size = 0;
STARTUPINFOEX si = { sizeof(si) };
PROCESS_INFORMATION pi;
InitializeProcThreadAttributeList(0, 1, 0, &Size);
ULONG err = GetLastError();
if (err = ERROR_INSUFFICIENT_BUFFER)
{
si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)alloca(Size);
if (InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &Size))
{
if (UpdateProcThreadAttribute(si.lpAttributeList, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProcess, sizeof(hProcess), 0, 0) &&
CreateProcessW(lpApplicationName, lpCommandLine, 0, 0, 0,
EXTENDED_STARTUPINFO_PRESENT, 0, 0, &si.StartupInfo, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
err = GetLastError();
}
DeleteProcThreadAttributeList(si.lpAttributeList);
}
else
{
err = GetLastError();
}
}
else
{
err = GetLastError();
}
return err;
}
ULONG CreateProcessEx(LUID AuthenticationId,
PCWSTR lpApplicationName,
PWSTR lpCommandLine)
{
ULONG err = ERROR_NOT_FOUND;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32W pe = { sizeof(pe) };
ULONG rcb;
if (Process32First(hSnapshot, &pe))
{
err = ERROR_NOT_FOUND;
BOOL found = FALSE;
do
{
if (pe.th32ProcessID && pe.th32ParentProcessID)
{
if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION|PROCESS_CREATE_PROCESS, FALSE, pe.th32ProcessID))
{
HANDLE hToken;
if (OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
{
TOKEN_STATISTICS ts;
if (GetTokenInformation(hToken, TokenStatistics, &ts, sizeof(ts), &rcb))
{
if (ts.AuthenticationId.LowPart == AuthenticationId.LowPart &&
ts.AuthenticationId.HighPart == AuthenticationId.HighPart)
{
found = TRUE;
err = CreateProcessEx(hProcess,
lpApplicationName,
lpCommandLine);
}
}
CloseHandle(hToken);
}
CloseHandle(hProcess);
}
}
} while (!found && Process32Next(hSnapshot, &pe));
}
else
{
err = GetLastError();
}
CloseHandle(hSnapshot);
}
else
{
err = GetLastError();
}
return err;
}
ULONG CreateProcessEx(PCWSTR lpApplicationName,
PWSTR lpCommandLine)
{
HANDLE hToken;
ULONG err = NOERROR;
if (OpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken))
{
union {
TOKEN_ELEVATION_TYPE tet;
TOKEN_LINKED_TOKEN tlt;
};
ULONG rcb;
if (GetTokenInformation(hToken, TokenElevationType, &tet, sizeof(tet), &rcb))
{
if (tet == TokenElevationTypeFull)
{
if (GetTokenInformation(hToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb))
{
TOKEN_STATISTICS ts;
BOOL fOk = GetTokenInformation(tlt.LinkedToken, TokenStatistics, &ts, sizeof(ts), &rcb);
CloseHandle(tlt.LinkedToken);
if (fOk)
{
err = CreateProcessEx(ts.AuthenticationId,
lpApplicationName,
lpCommandLine);
}
else
{
err = GetLastError();
}
}
else
{
err = GetLastError();
}
}
else
{
err = ERROR_ALREADY_ASSIGNED;
}
}
else
{
err = GetLastError();
}
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
return err;
}
and test:
WCHAR ApplicationName[MAX_PATH];
if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
{
WCHAR cmdline[] = L"cmd.exe /k whoami /priv /groups\r\n";
CreateProcessEx(ApplicationName, cmdline);
RunNonElevated(ApplicationName, cmdline);
}
for way #2 theoretical we can not found process with same logon id (AuthenticationId) as in our linked token. but way #1 always must work. always exist system process which have SeTcbPrivilege (for get primary form of linked token) + SeAssignPrimaryTokenPrivilege (for CreateProcessAsUser) (SeIncreaseQuotaPrivilege listen in msdn as typical require for CreateProcessAsUser but in my test this worked even if this privilege not enabled ). however all system processes (running as LocalSystem) have this 3 privilege in token (begin from smss.exe) and some system processes always run in system.
so way #1 must never fail and preferred. also we can here use for example inherited handles from our process, for interact with child process. this is impossible in way #2. it shown rather for completeness of the picture
at begin we check TOKEN_ELEVATION_TYPE and do job, only if it is TokenElevationTypeFull. in case TokenElevationTypeLimited we not elevated process - so nothing todo.
case TokenElevationTypeDefault mean or UAC if off (LUA disabled) or we run as built-in Administrator, and lua not filter tokens for this account (so all processes is "elevated" or more exactly it tokens not filtered via CreateRestrictedToken(..LUA_TOKEN..) ) - in this case also no sense try run "not elevated" process under this user
I am testing the following code in C using Win32 API, which is intended to create a new file that is accessible for the current user but private (not accessible) for everyone else.
For this this a deny all permissions for everyone SID, then for current's user SID I set up the permissions.
The file is created successfully and the permissions are apparently set up successfully (see screenshots below), however when I try to open the file with notepad, it says "access is denied" (My file explorer is running under the same session), also if I open a command prompt and do "type file_created.txt" the same "access is denied" appear.
I can of course, restore manually the permissions since I am administrator, but the idea is to make it work programmatically.
Image with everyone permissions:
Image with current user permissions:
The code:
#include <windows.h>
#include <AccCtrl.h>
#include <aclapi.h>
#include <stdio.h>
#include <stdexcept>
#include <string>
#undef UNICODE
int GetCurrentUserSid(PSID* pSID)
{
const int MAX_NAME = 256;
DWORD i, dwSize = 0;
HANDLE hToken;
PTOKEN_USER user;
TOKEN_INFORMATION_CLASS TokenClass = TokenUser;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ | TOKEN_QUERY, &hToken))
return GetLastError();
else
wprintf(L"OpenProcessToken() - got the handle to the access token!\n");
if (!GetTokenInformation(hToken, TokenClass, NULL, 0, &dwSize))
{
DWORD dwResult = GetLastError();
if (dwResult != ERROR_INSUFFICIENT_BUFFER)
{
wprintf(L"GetTokenInformation() failed, error %u\n", dwResult);
return FALSE;
}
else
wprintf(L"GetTokenInformation() - have an ample buffer...\n");
}
else
wprintf(L"GetTokenInformation() - buffer for Token group is OK\n");
user = (PTOKEN_USER)LocalAlloc(GPTR, dwSize);
if (!GetTokenInformation(hToken, TokenClass, user, dwSize, &dwSize))
{
wprintf(L"GetTokenInformation() failed, error %u\n", GetLastError());
return FALSE;
}
else
wprintf(L"GetTokenInformation() for getting the TokenGroups is OK\n");
DWORD dw_sid_len = GetLengthSid(user->User.Sid);
*pSID = (SID*)LocalAlloc(GPTR, dw_sid_len);
CopySid(dw_sid_len, *pSID, user->User.Sid);
return 0;
}
DWORD set_file_security(LPSTR filename)
{
PACL pNewDACL = NULL;
PSID current_user = NULL;
DWORD sid_size = SECURITY_MAX_SID_SIZE;
SID everyone_sid;
DWORD dwRes;
if (CreateWellKnownSid(WinWorldSid, NULL, &everyone_sid, &sid_size) ==
FALSE) {
throw std::runtime_error("CreateWellKnownSid() failed: " +
std::to_string(GetLastError()));
}
GetCurrentUserSid(¤t_user);
EXPLICIT_ACCESSA ea[2];
ZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESSA));
ea[0].grfAccessPermissions = ACCESS_SYSTEM_SECURITY | READ_CONTROL | WRITE_DAC | GENERIC_ALL;
ea[0].grfAccessMode = GRANT_ACCESS;
ea[0].grfInheritance = NO_INHERITANCE;
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[0].Trustee.ptstrName = reinterpret_cast<char*>(current_user);
ea[1].grfAccessPermissions = ACCESS_SYSTEM_SECURITY | READ_CONTROL | WRITE_DAC | GENERIC_ALL;
ea[1].grfAccessMode = DENY_ACCESS;
ea[1].grfInheritance = NO_INHERITANCE;
ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[1].Trustee.ptstrName = reinterpret_cast<char*>(&everyone_sid);
dwRes = SetEntriesInAclA(2, ea, NULL, &pNewDACL);
if (ERROR_SUCCESS != dwRes) {
printf("SetEntriesInAcl Error %u\n", dwRes);
//TODO: goto Cleanup;
}
PSECURITY_DESCRIPTOR pSD = NULL;
// Initialize a security descriptor.
pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR,
SECURITY_DESCRIPTOR_MIN_LENGTH);
if (NULL == pSD)
{
_tprintf(_T("LocalAlloc Error %u\n"), GetLastError());
goto Cleanup;
}
if (!InitializeSecurityDescriptor(pSD,
SECURITY_DESCRIPTOR_REVISION))
{
_tprintf(_T("InitializeSecurityDescriptor Error %u\n"),
GetLastError());
goto Cleanup;
}
// Add the ACL to the security descriptor.
if (!SetSecurityDescriptorDacl(pSD,
TRUE, // bDaclPresent flag
pNewDACL,
FALSE)) // not a default DACL
{
_tprintf(_T("SetSecurityDescriptorDacl Error %u\n"),
GetLastError());
goto Cleanup;
}
SECURITY_ATTRIBUTES sa;
// Initialize a security attributes structure.
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;
HANDLE hFile = CreateFileA(filename, GENERIC_ALL, 0, &sa, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
CloseHandle(hFile);
//dwRes = SetNamedSecurityInfoA(filename, SE_FILE_OBJECT,
// DACL_SECURITY_INFORMATION, NULL, NULL, pNewDACL, NULL);
//if (ERROR_SUCCESS != dwRes) {
// printf("SetNamedSecurityInfo Error %u\n", dwRes);
// //goto Cleanup;
//}
Cleanup:
if (pNewDACL != NULL)
LocalFree((HLOCAL)pNewDACL);
return dwRes;
}
int main()
{
//return 0;
// Create Everyone SID.
DWORD sid_size = SECURITY_MAX_SID_SIZE;
SID everyone_sid;
if (CreateWellKnownSid(WinWorldSid, NULL, &everyone_sid, &sid_size) ==
FALSE) {
throw std::runtime_error("CreateWellKnownSid() failed: " +
std::to_string(GetLastError()));
}
LPSTR filename = "created_file.txt";
set_file_security(filename);
return 0;
}
NOTE: I realized the code has memory leaks and other issues, I was just quickly hacking to test the idea.
In the Windows OS explicit deny permissions have a precedence over explicit allow permissions. So since "Everyone" group includes your account, access to the file is denied, even if you enabled it for yourself. In fact you don't need deny rule altogether, if access rights are not set in the object ACL for some user, access will be denied by default.
I was writing a c++ program to add ACE for object access Audit to SASL. Though all the functions return success, When I go and check the properties of the folder manually, I could not see any policy has been set.
Below is my code. I have modified the sample code given in MSDN site at the below link to add to SASL instead of DACL .
https://msdn.microsoft.com/en-us/library/windows/desktop/aa379283(v=vs.85).aspx
BOOL SetPrivilege(
HANDLE hToken, // access token handle
LPCTSTR lpszPrivilege, // name of privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
TOKEN_PRIVILEGES tp;
LUID luid;
if (!LookupPrivilegeValue(
NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid)) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError());
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
if (!AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES)NULL,
(PDWORD)NULL))
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError());
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
DWORD AddAceToObjectsSecurityDescriptor(
LPTSTR pszObjName, // name of object
SE_OBJECT_TYPE ObjectType, // type of object
LPTSTR pszTrustee // trustee for new ACE
)
{
DWORD dwRes = 0;
PACL pOldSACL = NULL, pNewSACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
EXPLICIT_ACCESS ea;
HANDLE hToken;
if (NULL == pszObjName)
return ERROR_INVALID_PARAMETER;
// Open a handle to the access token for the calling process.
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES,
&hToken))
{
printf("OpenProcessToken failed: %u\n", GetLastError());
goto Cleanup;
}
// Enable the SE_SECURITY_NAME privilege.
if (!SetPrivilege(hToken, SE_SECURITY_NAME, TRUE))
{
printf("You must be logged on as Administrator.\n");
goto Cleanup;
}
// Get a pointer to the existing SACL.
dwRes = GetNamedSecurityInfo(pszObjName, ObjectType,
SACL_SECURITY_INFORMATION,
NULL, NULL, NULL, &pOldSACL, &pSD);
if (ERROR_SUCCESS != dwRes) {
printf("GetNamedSecurityInfo Error %u\n", dwRes);
goto Cleanup;
}
// Initialize an EXPLICIT_ACCESS structure for the new ACE.
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
//ea.grfAccessPermissions = dwAccessRights;
ea.grfAccessPermissions = GENERIC_ALL;
//ea.grfAccessMode = AccessMode;
ea.grfAccessMode = SET_AUDIT_SUCCESS;
//ea.grfInheritance = dwInheritance;
ea.grfInheritance = INHERIT_ONLY;
//ea.Trustee.TrusteeForm = TrusteeForm;
ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
ea.Trustee.ptstrName = pszTrustee;
ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
// Create a new ACL that merges the new ACE
// into the existing SACL.
dwRes = SetEntriesInAcl(1, &ea, pOldSACL, &pNewSACL);
if (ERROR_SUCCESS != dwRes) {
printf("SetEntriesInAcl Error %u\n", dwRes);
goto Cleanup;
}
// Attach the new ACL as the object's SACL.
dwRes = SetNamedSecurityInfo(pszObjName, ObjectType,
SACL_SECURITY_INFORMATION,
NULL, NULL, NULL, pNewSACL);
if (ERROR_SUCCESS != dwRes) {
printf("SetNamedSecurityInfo Error %u\n", dwRes);
goto Cleanup;
}
// Disable the SE_SECURITY_NAME privilege.
if (!SetPrivilege(hToken, SE_SECURITY_NAME, FALSE))
{
printf("You must be logged on as Administrator.\n");
goto Cleanup;
}
Cleanup:
if (pSD != NULL)
LocalFree((HLOCAL)pSD);
if (pNewSACL != NULL)
LocalFree((HLOCAL)pNewSACL);
return dwRes;
}
int _tmain(int argc, _TCHAR* argv[])
{
LPTSTR objstrname = L"C:\\path\\to\\folder\\Test_Folder";
LPTSTR trusteeName = L"UserName"; // I have mentioned username here
AddAceToObjectsSecurityDescriptor(objstrname, SE_FILE_OBJECT, trusteeName);
return 0;
}
Though all the functions return success, I am not able to see any new audit policy is getting set. Might I am setting the parameters wrong, I that case I expect the functions to fail. Please help to resolve the issue.
I believe the problem is that you are setting the wrong inheritance flags.
INHERIT_ONLY means that the ACE should not apply to the object, but only be inherited by child objects.
However, you have not set either CONTAINER_INHERIT_ACE or OBJECT_INHERIT_ACE. So the ACE does not apply to child objects.
Since the ACE applies to neither the parent nor to children, it has no effect, so Windows discards it.
I've recently been working on an application that needs to establish an FTP connection with a server and download/upload files from it. For performance reasons, I would like to download multiple files at a time. For that reason I've tried to implement asynchronous operation on the Wininet API using the InternetOpen function along with the INTERNET_FLAG_ASYNC flag, as well as the InternetSetStatusCallback function. Here is a sample of my code, where I want to list all the files in the main directory of the remote server recursively:
/*Global variables*/
HANDLE MayContinue=0;
DWORD LatestResult=1;
/*Prototypes*/
void CALLBACK CallbackFunction(HINTERNET,DWORD_PTR,DWORD,LPVOID,DWORD);
//Iteration function called by main()
void FTPIterate()
{
WIN32_FIND_DATAA data;
HINTERNET Internet;
INTERNET_STATUS_CALLBACK call;
HINTERNET h_file;
MayContinue = ::CreateEvent (NULL, FALSE, FALSE, NULL);
iconnect=InternetOpen(NULL,INTERNET_OPEN_TYPE_PROXY,proxy_url,NULL,INTERNET_FLAG_ASYNC);
call=InternetSetStatusCallback(iconnect,(INTERNET_STATUS_CALLBACK)CallbackFunction);
while(f[FLAG_FTP_ITERATE])
{
MayContinue = ::CreateEvent(NULL,FALSE,FALSE,NULL);
InternetConnect(iconnect,ftp_url,INTERNET_DEFAULT_FTP_PORT,ftp_user,ftp_pass,INTERNET_SERVICE_FTP,NULL,LatestResult);
WaitForSingleObject (MayContinue, INFINITE);
server=(HINTERNET)LatestResult;
printf("Server handle: %i\n",(int)server);
printf("Server Error: %i\n",GetLastError());
SetLastError(0);
MayContinue = ::CreateEvent(NULL,FALSE,FALSE,NULL);
FtpFindFirstFile(server,ftp_base,&data,INTERNET_FLAG_NO_CACHE_WRITE,LatestResult);
WaitForSingleObject(MayContinue,INFINITE);
h_file=(HINTERNET)LatestResult;
//do stuff
printf("FindFirstFile handle: %i\n",(int)h_File);
while((MayContinue = ::CreateEvent(NULL,FALSE,FALSE,NULL)) && InternetFindNextFileA(h_file,&data))
{
WaitForSingleObject(MayContinue,INFINITE);
//do stuff
}
printf("FindNextFile Error: %i\n",GetLastError()); //loop is never executed
InternetCloseHandle(server);
}
}
void CALLBACK CallbackFunction(HINTERNET hInternet,DWORD_PTR dwContext,DWORD dwInternetStatus,LPVOID lpvStatusInformation,DWORD dwStatusInformationLength)
{
if (dwInternetStatus == INTERNET_STATUS_REQUEST_COMPLETE)
{
LatestResult = ((LPINTERNET_ASYNC_RESULT)lpvStatusInformation)->dwResult;
SetEvent (MayContinue);
}
}
My code is based on this post from Stack Overflow. When I run it, I first of all get an error after the call to InternetConnect, which is ERROR_IO_PENDING. According to the WinAPI reference this means that there's still some operation being performed. Shouldn't the call to WaitForSingleObject prevent this from happening? (actually, the HINTERNET handle returned by InternetConnect seems to be valid).
When I call the FtpFindFirstFile function it correctly retrieves the first file, but when I use the HINTERNET handle (which, again, seems to be valid) returned by it in the InternetFindNextFile function it fails with error INVALID_HANDLE_VALUE.
EDIT: I egt those errors when using Remy's code:
Connect Handle 00CC0004
Waiting for server handle
Unable to find first file. OS Error: 6 //aren't those calls to FindFirstFile weird if InternetConnect hasn't returned yet?
Waiting for server handle
Unable to find first file. OS Error: 6
Waiting for server handle
Unable to find first file. OS Error: 6
Waiting for server handle
Unable to find first file. OS Error: 6
Waiting for server handle
Unable to find first file. OS Error: 6
Waiting for server handle
Unable to find first file. OS Error: 6
Waiting for server handle.
Unable to connect to Server. Inet Error: 12015 Waiting for server handle
Can someone help me find the mistake?
Thank you in advance.
The ERROR_IO_PENDING error is coming from InternetOpen() itself. Since WaitForSingleObject() is succeeding, it is not overwriting GetLastError() (it only does so on error, as most APIs do), so the error is being carried over from the result of InternetOpen(). This is not the correct way to use GetLastError(). Assume that all APIs overwrite GetLastError() (if documented to use GetLastError() at all), and make sure you call it immediately only if an API fails (unless documented as being used during success conditions).
What your code is doing is NOT asynchronous! You are issuing asynchronous API calls, but you are waiting for their results, which defeats the purpose. Your code is acting synchronously, the same as if you were to omit the INTERNAL_FLAG_ASYNC flag and WaitForSingleObject() calls (not to mention you are leaking event resource by calling CreateEvent() unnecessarily), eg:
void LogError(const char *Op)
{
DWORD err = GetLastError();
if (err == ERROR_INTERNET_EXTENDED_ERROR)
{
LPSTR szBuffer;
DWORD dwLength = 0;
InternetGetLastResponseInfoA(&err, NULL, &dwLength);
if (GetLastError() != INSUFFICIENT_BUFFER)
{
printf("%s. Unknown Inet Error. OS Error: %u", Op, GetLastError());
return;
}
szBuffer = new char[dwLength+1];
InternetGetLastResponseInfoA(&err, szBuffer, &dwLength);
szBuffer[dwLength] = 0;
printf("%s. Inet Error: %u %s", Op, err, szBuffer);
delete[] szBuffer;
}
else
{
LPSTR lpBuffer = NULL;
DWORD dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, (LPSTR)&lpBuffer, 0, NULL);
if (lpBuffer)
{
printf("%s. OS Error: %u %s", Op, err, lpBuffer);
LocalFree(lpBuffer);
}
else
printf("%s. OS Error: %u", Op, err);
}
printf("\n");
}
void FTPIterate()
{
WIN32_FIND_DATAA data;
HINTERNET hConnect;
HINTERNET hServer;
HINTERNET hFile;
hConnect = InternetOpen(NULL, INTERNET_OPEN_TYPE_PROXY, proxy_url, NULL, 0);
if (hConnect == NULL)
{
LogError("Unable to Open Internet");
return;
}
printf("Connect handle: %p\n", hConnect);
while (f[FLAG_FTP_ITERATE])
{
printf("Connecting to Server\n");
hServer = InternetConnect(hConnect, ftp_url, INTERNET_DEFAULT_FTP_PORT, ftp_user, ftp_pass, INTERNET_SERVICE_FTP, NULL, 0);
if (hServer == NULL)
{
LogError("Unable to connect to Server");
continue;
}
printf("Connected to Server. Server handle: %p\n", hServer);
printf("Finding first file\n");
hFile = FtpFindFirstFileA(hServer, ftp_base, &data, INTERNET_FLAG_NO_CACHE_WRITE, 0);
if (hFile == NULL)
{
if (GetLastError() == ERROR_NO_MORE_FILES)
printf("No files were found\n");
else
LogError("Unable to find first file");
}
else
{
printf("Find handle: %p\n", hFile);
do
{
//do stuff
printf("Finding next file\n");
if (!InternetFindNextFileA(hFile, &data))
{
if (GetLastError() == ERROR_NO_MORE_FILES)
printf("No more files were found\n");
else
LogError("Unable to find next file")
break;
}
}
while (true);
InternetCloseHandle(hFile);
}
InternetCloseHandle(hServer);
}
InternetCloseHandle(hConnect);
}
To make this code run asynchronously, get rid of all off the waits and implement a state machine that your callback advances as needed, eg:
enum FTPState {ftpConnect, ftpWaitingForConnect, ftpFindFirstFile, ftpWaitingForFirstFind, ftpFindNextFile, ftpWaitingForNextFind, ftpProcessFile, ftpDisconnect};
struct REQ_CONTEXT
{
FTPState State;
WIN32_FIND_DATAA data;
HINTERNET hConnect;
HINTERNET hServer;
HINTERNET hFile;
HANDLE hDoneEvent;
};
void LogError(const char *Op, DWORD err)
{
if (err == ERROR_INTERNET_EXTENDED_ERROR)
{
LPSTR szBuffer;
DWORD dwLength = 0;
InternetGetLastResponseInfoA(&err, NULL, &dwLength);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
printf("%s. Unknown Inet Error. OS Error: %u", Op, GetLastError());
return;
}
szBuffer = new char[dwLength+1];
InternetGetLastResponseInfoA(&err, szBuffer, &dwLength);
szBuffer[dwLength] = 0;
printf("%s. Inet Error: %u %s", Op, err, szBuffer);
delete[] szBuffer;
}
else
{
LPSTR lpBuffer = NULL;
DWORD dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, (LPSTR)&lpBuffer, 0, NULL);
if (lpBuffer)
{
printf("%s. OS Error: %u %s", Op, err, lpBuffer);
LocalFree(lpBuffer);
}
else
printf("%s. OS Error: %u", Op, err);
}
printf("\n");
}
void LogError(const char *Op)
{
LogError(Op, GetLastError());
}
void DoNextStep(REQ_CONTEXT *ctx)
{
do
{
if ((ctx->State == ftpConnect) && (!f[FLAG_FTP_ITERATE]))
{
printf("Done!\n");
SetEvent(ctx->hDoneEvent);
return;
}
switch (ctx->State)
{
case ftpConnect:
{
printf("Connecting to Server\n");
HINTERNET hServer = InternetConnect(ctx->hConnect, ftp_url, INTERNET_DEFAULT_FTP_PORT, ftp_user, ftp_pass, INTERNET_SERVICE_FTP, NULL, (DWORD_PTR)ctx);
if (hServer != NULL)
{
if (ctx->hServer == NULL)
{
ctx->hServer = hServer;
printf("Server handle: %p\n", ctx->hServer);
}
printf("Connected to Server\n");
ctx->State = ftpFindFirstFile;
}
else if (GetLastError() == ERROR_IO_PENDING)
{
if (ctx->hServer == NULL)
printf("Waiting for Server handle\n");
printf("Waiting for Server connect to complete\n");
ctx->State = ftpWaitingForConnect;
}
else
LogError("Unable to connect to Server");
break;
}
case ftpWaitingForConnect:
return;
case ftpFindFirstFile:
{
printf("Finding first file\n");
HINTERNET hFile = FtpFindFirstFileA(ctx->hServer, ftp_base, &ctx->data, INTERNET_FLAG_NO_CACHE_WRITE, (DWORD_PTR)ctx);
if (hFile != NULL)
{
if (ctx->hFile == NULL)
{
ctx->hFile = hFile;
printf("Find handle: %p\n", ctx->hFile);
}
ctx->State = ftpProcessFile;
}
else if (GetLastError() == ERROR_IO_PENDING)
{
if (ctx->hFile == NULL)
printf("Waiting for Find handle\n");
printf("Waiting for Find to complete\n");
ctx->State = ftpWaitingForFirstFind;
}
else
{
if (GetLastError() == ERROR_NO_MORE_FILES)
printf("No files were found\n");
else
LogError("Unable to find first file");
ctx->State = ftpDisconnect;
}
break;
}
case ftpWaitingForFirstFind:
case ftpWaitingForNextFind:
return;
case ftpProcessFile:
{
//do stuff
printf("Finding next file\n");
if (!InternetFindNextFileA(ctx->hFile, &ctx->data))
{
if (GetLastError() == ERROR_IO_PENDING)
{
printf("Waiting for next file to complete\n");
ctx->State = ftpWaitingForNextFind;
}
else
{
if (GetLastError() == ERROR_NO_MORE_FILES)
printf("No more files were found\n");
else
LogError("Unable to find next file");
ctx->State = ftpDisconnect;
}
}
break;
}
case ftpDisconnect:
{
printf("Disconnecting\n");
if (ctx->hFile != NULL)
{
InternetCloseHandle(ctx->hFile);
ctx->hFile = NULL;
}
if (ctx->hServer != NULL)
{
InternetCloseHandle(ctx->hServer);
ctx->hServer = NULL;
}
ctx->State = ftpConnect;
break;
}
}
}
while (true);
}
void CALLBACK CallbackFunction(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
{
REQ_CONTEXT *ctx = (REQ_CONTEXT*) dwContext;
switch (dwInternetStatus)
{
case INTERNET_STATUS_HANDLE_CREATED:
{
LPINTERNET_ASYNC_RESULT Result = (LPINTERNET_ASYNC_RESULT) lpvStatusInformation;
switch (ctx->State)
{
case ftpConnect:
case ftpWaitingForConnect:
ctx->hServer = (HINTERNET) Result->dwResult;
printf("Server handle: %p\n", ctx->hServer);
break;
case ftpFindFirstFile:
case ftpWaitingForFirstFind:
ctx->hFile = (HINTERNET) Result->dwResult;
printf("Find handle: %p\n", ctx->hFile);
break;
}
break;
}
case INTERNET_STATUS_REQUEST_COMPLETE:
{
LPINTERNET_ASYNC_RESULT Result = (LPINTERNET_ASYNC_RESULT) lpvStatusInformation;
switch (ctx->State)
{
case ftpWaitingForConnect:
{
if (!Result->dwResult)
{
LogError("Unable to connect to Server", Result->dwError);
ctx->State = ftpDisconnect;
}
else
{
printf("Connected to Server\n");
ctx->State = ftpFindFirstFile;
}
break;
}
case ftpWaitingForFirstFind:
case ftpWaitingForNextFind:
{
if (!Result->dwResult)
{
if (Result->dwError == ERROR_NO_MORE_FILES)
printf("No %sfiles were found\n", (ctx->State == ftpWaitingForNextFind) ? "more " : "");
else if (ctx->State == ftpWaitingForFirstFind)
LogError("Unable to find first file", Result->dwError);
else
LogError("Unable to find next file", Result->dwError);
ctx->State = ftpDisconnect;
}
else
ctx->State = ftpProcessFile;
break;
}
}
DoNextStep(ctx);
break;
}
}
}
void FTPIterate()
{
REQ_CONTEXT ctx = {0};
ctx.State = ftpConnect;
ctx.hDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (ctx.hDoneEvent == NULL)
{
LogError("Unable to Create Done Event");
return;
}
ctx.hConnect = InternetOpen(NULL, INTERNET_OPEN_TYPE_PROXY, proxy_url, NULL, INTERNET_FLAG_ASYNC);
if (ctx.hConnect == NULL)
{
LogError("Unable to Open Internet");
CloseHandle(ctx.hDoneEvent);
return;
}
printf("Connect handle: %p\n", ctx.hConnect);
InternetSetStatusCallback(ctx.hConnect, &CallbackFunction);
DoNextStep(&ctx);
WaitForSingleObject(ctx.hDoneEvent, INFINITE);
InternetCloseHandle(ctx.hConnect);
CloseHandle(ctx.hDoneEvent);
}
Following my previous question here, now I get two shells - one parent (not elevated), and a child shell (elevated). What does one need to do to the code to have instead one shell only that's elevated? E.g. how about somehow closing the parent process?
BOOL IsAppRunningAsAdminMode()
{
BOOL fIsRunAsAdmin = FALSE;
DWORD dwError = ERROR_SUCCESS;
PSID pAdministratorsGroup = NULL;
// Allocate and initialize a SID of the administrators group.
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
if (!AllocateAndInitializeSid(
&NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pAdministratorsGroup))
{
dwError = GetLastError();
goto Cleanup;
}
// Determine whether the SID of administrators group is enabled in
// the primary access token of the process.
if (!CheckTokenMembership(NULL, pAdministratorsGroup, &fIsRunAsAdmin))
{
dwError = GetLastError();
goto Cleanup;
}
Cleanup:
// Centralized cleanup for all allocated resources.
if (pAdministratorsGroup)
{
FreeSid(pAdministratorsGroup);
pAdministratorsGroup = NULL;
}
// Throw the error if something failed in the function.
if (ERROR_SUCCESS != dwError)
{
throw dwError;
}
return fIsRunAsAdmin;
}
int main() {
bool fIsRunAsAdmin = IsAppRunningAsAdminMode();
if (fIsRunAsAdmin == false)
{
wchar_t szPath[MAX_PATH];
if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)))
{
// Launch itself as admin
SHELLEXECUTEINFO sei = { sizeof(sei) };
sei.lpVerb = L"runas";
sei.lpFile = szPath;
sei.hwnd = NULL;
sei.nShow = SW_NORMAL;
if (!ShellExecuteEx(&sei))
{
DWORD dwError = GetLastError();
if (dwError == ERROR_CANCELLED)
{
// The user refused to allow privileges elevation.
std::cout << "User did not allow elevation" << std::endl;
}
}
}
}
else {
//do nothing since process already elevated
}
//other code following omitted