446 lines
12 KiB
C
446 lines
12 KiB
C
/*
|
|
Inno Setup
|
|
Copyright (C) 1997-2008 Jordan Russell
|
|
Portions by Martijn Laan
|
|
For conditions of distribution and use, see LICENSE.TXT.
|
|
|
|
64-bit helper process
|
|
|
|
Compiled on Visual Studio 2005 SP1
|
|
Tested on x64 and IA-64 architectures (Athlon 64 and Merced specifically).
|
|
*/
|
|
|
|
#define _WIN32_IE 0x0600
|
|
#include <windows.h>
|
|
#include <commctrl.h>
|
|
#include <shlwapi.h>
|
|
#include <aclapi.h>
|
|
|
|
#ifndef UNICODE
|
|
#error UNICODE isn't defined
|
|
#endif
|
|
|
|
// As of Inno Setup 5.1.11, the helper is no longer used to register
|
|
// DLLs, so avoid linking in support for REQUEST_REGISTER_SERVER.
|
|
#undef IMPLEMENT_REGISTER_SERVER
|
|
|
|
#define HELPER_VERSION 105
|
|
|
|
// Request commands
|
|
#define REQUEST_PING 1
|
|
#define REQUEST_GRANT_PERMISSION 2
|
|
#ifdef IMPLEMENT_REGISTER_SERVER
|
|
#define REQUEST_REGISTER_SERVER 3
|
|
#endif
|
|
#define REQUEST_REGISTER_TYPE_LIBRARY 4
|
|
|
|
// These must be kept in synch with Struct.pas:
|
|
typedef struct {
|
|
SID_IDENTIFIER_AUTHORITY Authority;
|
|
BYTE SubAuthCount;
|
|
DWORD SubAuth[2];
|
|
} TGrantPermissionSid;
|
|
typedef struct {
|
|
TGrantPermissionSid Sid;
|
|
DWORD AccessMask;
|
|
} TGrantPermissionEntry;
|
|
|
|
// This value must be kept in synch with Struct.pas:
|
|
#define MAX_GRANT_PERMISSION_ENTRIES 32
|
|
|
|
// These must be kept in synch with Helper.pas:
|
|
typedef struct {
|
|
DWORD ObjectType;
|
|
DWORD EntryCount;
|
|
DWORD Inheritance;
|
|
WCHAR ObjectName[4096];
|
|
TGrantPermissionEntry Entries[MAX_GRANT_PERMISSION_ENTRIES];
|
|
} REQUEST_GRANT_PERMISSION_DATA;
|
|
|
|
typedef struct {
|
|
BOOL Unregister;
|
|
BOOL FailCriticalErrors;
|
|
WCHAR Filename[4096];
|
|
WCHAR Directory[4096];
|
|
} REQUEST_REGISTER_SERVER_DATA;
|
|
|
|
typedef struct {
|
|
BOOL Unregister;
|
|
WCHAR Filename[4096];
|
|
} REQUEST_REGISTER_TYPE_LIBRARY_DATA;
|
|
|
|
typedef struct {
|
|
DWORD SequenceNumber;
|
|
DWORD Command;
|
|
DWORD DataSize;
|
|
union {
|
|
BYTE Data[65536];
|
|
REQUEST_GRANT_PERMISSION_DATA GrantPermissionData;
|
|
REQUEST_REGISTER_SERVER_DATA RegisterServerData;
|
|
REQUEST_REGISTER_TYPE_LIBRARY_DATA RegisterTypeLibraryData;
|
|
};
|
|
} REQUEST_DATA;
|
|
|
|
typedef struct {
|
|
DWORD SequenceNumber;
|
|
DWORD StatusCode;
|
|
DWORD ErrorCode;
|
|
DWORD DataSize;
|
|
BYTE Data[65536];
|
|
} RESPONSE_DATA;
|
|
|
|
static TCHAR SystemDir[MAX_PATH+1];
|
|
|
|
static DWORD GrantPermission(const DWORD ObjectType, const LPWSTR ObjectName,
|
|
TGrantPermissionEntry *Entries, const DWORD EntryCount,
|
|
const DWORD Inheritance)
|
|
{
|
|
DWORD ErrorCode;
|
|
PSECURITY_DESCRIPTOR SD;
|
|
PACL Dacl, NewDacl;
|
|
EXPLICIT_ACCESSW ExplicitAccess[MAX_GRANT_PERMISSION_ENTRIES];
|
|
DWORD I;
|
|
|
|
// Note: SecureZeroMemory is used because memset/ZeroMemory aren't available
|
|
SecureZeroMemory(&ExplicitAccess, sizeof(ExplicitAccess));
|
|
|
|
if (EntryCount > ARRAYSIZE(ExplicitAccess)) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
ErrorCode = GetNamedSecurityInfoW(ObjectName, ObjectType,
|
|
DACL_SECURITY_INFORMATION, NULL, NULL, &Dacl, NULL, &SD);
|
|
if (ErrorCode != ERROR_SUCCESS) {
|
|
return ErrorCode;
|
|
}
|
|
|
|
// From here on, use "goto out" instead of "return"
|
|
|
|
for (I = 0; I < EntryCount; I++) {
|
|
TGrantPermissionEntry *E = &Entries[I];
|
|
PSID Sid;
|
|
|
|
if (!AllocateAndInitializeSid(&E->Sid.Authority, E->Sid.SubAuthCount,
|
|
E->Sid.SubAuth[0], E->Sid.SubAuth[1], 0, 0, 0, 0, 0, 0, &Sid)) {
|
|
ErrorCode = GetLastError();
|
|
if (ErrorCode == 0) ErrorCode = ERROR_INVALID_PARAMETER; // just in case
|
|
goto out;
|
|
}
|
|
ExplicitAccess[I].grfAccessPermissions = E->AccessMask;
|
|
ExplicitAccess[I].grfAccessMode = GRANT_ACCESS;
|
|
ExplicitAccess[I].grfInheritance = Inheritance;
|
|
ExplicitAccess[I].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ExplicitAccess[I].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
|
|
ExplicitAccess[I].Trustee.ptstrName = Sid;
|
|
}
|
|
|
|
ErrorCode = SetEntriesInAclW(EntryCount, ExplicitAccess, Dacl, &NewDacl);
|
|
if (ErrorCode == ERROR_SUCCESS) {
|
|
ErrorCode = SetNamedSecurityInfoW(ObjectName, ObjectType,
|
|
DACL_SECURITY_INFORMATION, NULL, NULL, NewDacl, NULL);
|
|
LocalFree(NewDacl);
|
|
}
|
|
|
|
out:
|
|
for (I = 0; I < EntryCount; I++) {
|
|
PSID Sid = ExplicitAccess[I].Trustee.ptstrName;
|
|
if (Sid) FreeSid(Sid);
|
|
}
|
|
LocalFree(SD);
|
|
|
|
return ErrorCode;
|
|
}
|
|
|
|
#ifdef IMPLEMENT_REGISTER_SERVER
|
|
static DWORD RegisterServer(const BOOL Unregister, const LPWSTR Filename,
|
|
const LPWSTR Directory, const BOOL FailCriticalErrors, DWORD *ErrorCode)
|
|
{
|
|
DWORD retval = 0;
|
|
HRESULT OleInitResult;
|
|
UINT SaveErrorMode;
|
|
HANDLE LibHandle;
|
|
|
|
// Initialize OLE. Regsvr32 does this.
|
|
OleInitResult = OleInitialize(NULL);
|
|
if (FAILED(OleInitResult)) {
|
|
*ErrorCode = OleInitResult;
|
|
return 4;
|
|
}
|
|
|
|
SaveErrorMode = SetErrorMode(FailCriticalErrors ?
|
|
SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS :
|
|
SEM_NOOPENFILEERRORBOX);
|
|
|
|
// If no directory was supplied, we use the system directory.
|
|
// Relative paths are assumed to be relative to the system directory.
|
|
SetCurrentDirectory(SystemDir);
|
|
if (*Directory) {
|
|
SetCurrentDirectoryW(Directory);
|
|
}
|
|
|
|
LibHandle = LoadLibraryExW(Filename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
|
|
if (LibHandle) {
|
|
typedef HRESULT (STDAPICALLTYPE *DLLREGISTERSERVER)(void);
|
|
DLLREGISTERSERVER RegisterServerProc;
|
|
|
|
RegisterServerProc = (DLLREGISTERSERVER)GetProcAddress(LibHandle,
|
|
Unregister ? "DllUnregisterServer" : "DllRegisterServer");
|
|
if (RegisterServerProc) {
|
|
*ErrorCode = (*RegisterServerProc)();
|
|
retval = 3;
|
|
} else {
|
|
*ErrorCode = GetLastError();
|
|
retval = 2;
|
|
}
|
|
|
|
FreeLibrary(LibHandle);
|
|
} else {
|
|
*ErrorCode = GetLastError();
|
|
retval = 1;
|
|
}
|
|
|
|
// Revert the current directory and error mode changes
|
|
SetCurrentDirectory(SystemDir);
|
|
SetErrorMode(SaveErrorMode);
|
|
|
|
OleUninitialize();
|
|
|
|
return retval;
|
|
}
|
|
#endif
|
|
|
|
static DWORD RegisterTypeLibrary(const BOOL Unregister,
|
|
const LPWSTR Filename, DWORD *ErrorCode)
|
|
{
|
|
DWORD retval;
|
|
HRESULT hr;
|
|
ITypeLib *TypeLib;
|
|
TLIBATTR *LibAttr;
|
|
|
|
retval = 1;
|
|
hr = LoadTypeLib(Filename, &TypeLib);
|
|
if (hr == S_OK) {
|
|
if (!Unregister) {
|
|
retval = 2;
|
|
hr = RegisterTypeLib(TypeLib, Filename, NULL);
|
|
} else {
|
|
retval = 3;
|
|
hr = TypeLib->lpVtbl->GetLibAttr(TypeLib, &LibAttr);
|
|
if (hr == S_OK) {
|
|
retval = 4;
|
|
hr = UnRegisterTypeLib(&LibAttr->guid, LibAttr->wMajorVerNum,
|
|
LibAttr->wMinorVerNum, LibAttr->lcid, LibAttr->syskind);
|
|
TypeLib->lpVtbl->ReleaseTLibAttr(TypeLib, LibAttr);
|
|
}
|
|
}
|
|
TypeLib->lpVtbl->Release(TypeLib);
|
|
}
|
|
|
|
*ErrorCode = hr;
|
|
return retval;
|
|
}
|
|
|
|
static void ProcessRequest(REQUEST_DATA *request, RESPONSE_DATA *response)
|
|
{
|
|
response->SequenceNumber = request->SequenceNumber;
|
|
response->StatusCode = 0; // 0 means "didn't execute command"
|
|
response->ErrorCode = 0;
|
|
response->DataSize = 0;
|
|
|
|
switch (request->Command) {
|
|
case REQUEST_PING:
|
|
response->StatusCode = 1;
|
|
break;
|
|
case REQUEST_GRANT_PERMISSION:
|
|
{
|
|
// response:
|
|
// StatusCode 1 if request was valid
|
|
// ErrorCode Error code from GrantPermission()
|
|
|
|
REQUEST_GRANT_PERMISSION_DATA *data = &request->GrantPermissionData;
|
|
|
|
if (request->DataSize == sizeof(*data) &&
|
|
data->EntryCount <= ARRAYSIZE(data->Entries)) {
|
|
response->ErrorCode = GrantPermission(data->ObjectType,
|
|
data->ObjectName, data->Entries, data->EntryCount,
|
|
data->Inheritance);
|
|
response->StatusCode = 1;
|
|
}
|
|
}
|
|
break;
|
|
#ifdef IMPLEMENT_REGISTER_SERVER
|
|
case REQUEST_REGISTER_SERVER:
|
|
{
|
|
// response:
|
|
// StatusCode 1 if LoadLibrary failed,
|
|
// 2 if GetProcAddress failed,
|
|
// 3 if Dll(Un)RegisterServer called; possibly succeeded,
|
|
// 4 if OleInitialize failed
|
|
// ErrorCode Error code or HRESULT depending on the StatusCode
|
|
|
|
REQUEST_REGISTER_SERVER_DATA *data = &request->RegisterServerData;
|
|
|
|
if (request->DataSize == sizeof(*data)) {
|
|
response->StatusCode = RegisterServer(data->Unregister,
|
|
data->Filename, data->Directory, data->FailCriticalErrors,
|
|
&response->ErrorCode);
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
case REQUEST_REGISTER_TYPE_LIBRARY:
|
|
{
|
|
// response:
|
|
// StatusCode 1 if LoadTypeLib didn't return S_OK
|
|
// Register only:
|
|
// 2 if RegisterTypeLib called; possibly succeeded
|
|
// Unregister only:
|
|
// 3 if ITypeLib::GetLibAttr didn't return S_OK
|
|
// 4 if UnRegisterTypeLib called; possibly succeeded
|
|
// ErrorCode An HRESULT
|
|
|
|
REQUEST_REGISTER_TYPE_LIBRARY_DATA *data = &request->RegisterTypeLibraryData;
|
|
|
|
if (request->DataSize == sizeof(*data)) {
|
|
response->StatusCode = RegisterTypeLibrary(data->Unregister,
|
|
data->Filename, &response->ErrorCode);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static BOOL WINAPI ConsoleCtrlHandlerRoutine(DWORD dwCtrlType)
|
|
{
|
|
// By default, during shutdown, applications without windows are killed
|
|
// unconditionally. This handler suppresses that default handling, and
|
|
// just ignores the termination request.
|
|
//
|
|
// Note: This can cause a "Process not responding" dialog to appear, but
|
|
// that's still better than dying unconditionally. I don't think it's
|
|
// possible to programmatically abort the shutdown sequence in a
|
|
// non-windowed application. (Windowed applications, of course, can
|
|
// return FALSE in response to WM_QUERYENDSESSION. But we have no message
|
|
// loop so creating a window is not an option.)
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
void Test(void)
|
|
{
|
|
TGrantPermissionEntry Entry = { 0 };
|
|
Entry.Sid.Authority.Value[5] = 1;
|
|
Entry.Sid.SubAuthCount = 1;
|
|
Entry.AccessMask = 0x1200A9;
|
|
GrantPermission(SE_FILE_OBJECT, "c:\\testfile.txt", &Entry, 1, 0);
|
|
}
|
|
*/
|
|
|
|
int Main(void)
|
|
{
|
|
int argc;
|
|
LPWSTR* argv;
|
|
LONGLONG llHandle;
|
|
HANDLE hPipe;
|
|
LONG retval = 0;
|
|
static REQUEST_DATA request;
|
|
static RESPONSE_DATA response;
|
|
|
|
// Work around bug in Windows XP Gold & SP1: If the application manifest
|
|
// specifies COMCTL32.DLL version 6.0 (to enable visual styles), we must
|
|
// call InitCommonControls() to ensure that we actually link to
|
|
// COMCTL32.DLL, otherwise calls to MessageBox() fail. (XP SP2 appears
|
|
// to fix this.)
|
|
// NOTE: This workaround was brought over from RegDLL for consistency,
|
|
// but may not actually be needed here since Setup (currently) refuses
|
|
// to spawn Helper on pre-5.02 SP1 versions of Windows.
|
|
InitCommonControls();
|
|
|
|
SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
GetSystemDirectory(SystemDir, ARRAYSIZE(SystemDir));
|
|
SetCurrentDirectory(SystemDir);
|
|
|
|
// Give us a lower-than-default shutdown priority so that if for some
|
|
// reason a shutdown is initiated while we're running, Windows
|
|
// "shouldn't" try to kill us before Setup.
|
|
SetProcessShutdownParameters(0x100, 0);
|
|
|
|
SetConsoleCtrlHandler(ConsoleCtrlHandlerRoutine, TRUE);
|
|
|
|
//
|
|
// Get version and message-mode pipe handle from command line
|
|
//
|
|
|
|
argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
|
if (argv == NULL) {
|
|
return MAKELONG(GetLastError(), 1);
|
|
}
|
|
if (argc != 3) {
|
|
return MAKELONG(0, 2);
|
|
}
|
|
if (StrToIntW(argv[1]) != HELPER_VERSION) {
|
|
return MAKELONG(0, 3);
|
|
}
|
|
if (!StrToInt64ExW(argv[2], STIF_SUPPORT_HEX, &llHandle)) {
|
|
return MAKELONG(0, 4);
|
|
}
|
|
hPipe = (HANDLE)(INT_PTR)llHandle;
|
|
|
|
//
|
|
// Wait for and process incoming requests
|
|
//
|
|
|
|
while (TRUE) {
|
|
DWORD dwBytesRead, dwResponseLength, dwBytesWritten;
|
|
|
|
if (!ReadFile(hPipe, &request, sizeof(request), &dwBytesRead, NULL)) {
|
|
// We can normally expect ReadFile to fail with ERROR_BROKEN_PIPE
|
|
// when the client has disconnected.
|
|
// Note: We don't bother handling ERROR_MORE_DATA because the client
|
|
// shouldn't be sending us oversized requests in the first place.
|
|
if (GetLastError() != ERROR_BROKEN_PIPE) {
|
|
retval = MAKELONG(GetLastError(), 5);
|
|
}
|
|
break;
|
|
}
|
|
if (dwBytesRead < FIELD_OFFSET(REQUEST_DATA, Data) ||
|
|
request.DataSize != dwBytesRead - FIELD_OFFSET(REQUEST_DATA, Data)) {
|
|
// Request message is too short or too long
|
|
retval = MAKELONG(0, 6);
|
|
break;
|
|
}
|
|
|
|
ProcessRequest(&request, &response);
|
|
|
|
dwResponseLength = FIELD_OFFSET(RESPONSE_DATA, Data) + response.DataSize;
|
|
if (!WriteFile(hPipe, &response, dwResponseLength, &dwBytesWritten, NULL)) {
|
|
// WriteFile could fail if the client disconnected for some reason.
|
|
retval = MAKELONG(GetLastError(), 7);
|
|
break;
|
|
}
|
|
if (dwBytesWritten != dwResponseLength) {
|
|
// Should never get here.
|
|
retval = MAKELONG(0, 8);
|
|
break;
|
|
}
|
|
}
|
|
|
|
CloseHandle(hPipe);
|
|
|
|
return retval;
|
|
}
|
|
|
|
int mainCRTStartup(void)
|
|
{
|
|
int retval;
|
|
|
|
retval = Main();
|
|
|
|
// Good idea to call ExitProcess because it'll terminate any other
|
|
// threads the system might've created. A simple "return" won't.
|
|
ExitProcess(retval);
|
|
|
|
return retval;
|
|
} |