diff --git a/build/VStudio/launcher/launcher.vcxproj b/build/VStudio/launcher/launcher.vcxproj
index 4f0c6a86..e687d74a 100644
--- a/build/VStudio/launcher/launcher.vcxproj
+++ b/build/VStudio/launcher/launcher.vcxproj
@@ -110,6 +110,7 @@
Console
true
true
+ %(AdditionalDependencies);rpcrt4.lib
@@ -128,6 +129,7 @@
Console
true
true
+ %(AdditionalDependencies);rpcrt4.lib
@@ -149,6 +151,7 @@
true
true
true
+ %(AdditionalDependencies);rpcrt4.lib
@@ -170,6 +173,7 @@
true
true
true
+ %(AdditionalDependencies);rpcrt4.lib
diff --git a/src/dll/np.c b/src/dll/np.c
index 80110ec2..602a8d19 100644
--- a/src/dll/np.c
+++ b/src/dll/np.c
@@ -18,6 +18,7 @@
#include
#include
#include
+#include
#define FSP_NP_NAME LIBRARY_NAME ".Np"
#define FSP_NP_TYPE ' spF' /* pick a value hopefully not in use */
@@ -403,7 +404,7 @@ DWORD APIENTRY NPAddConnection3(HWND hwndOwner,
LPNETRESOURCEW lpNetResource, LPWSTR lpPassword, LPWSTR lpUserName, DWORD dwFlags)
{
DWORD NpResult;
- WCHAR UserName[256], Password[256];
+ WCHAR UserName[CREDUI_MAX_USERNAME_LENGTH], Password[CREDUI_MAX_PASSWORD_LENGTH];
/* CONNECT_PROMPT is only valid if CONNECT_INTERACTIVE is also set */
if (CONNECT_PROMPT == (dwFlags & (CONNECT_INTERACTIVE | CONNECT_PROMPT)))
diff --git a/src/launcher/launchctl.c b/src/launcher/launchctl.c
index 6047c9bd..d48f4399 100644
--- a/src/launcher/launchctl.c
+++ b/src/launcher/launchctl.c
@@ -115,7 +115,8 @@ static int call_pipe_and_report(PWSTR PipeBuf, ULONG SendSize, ULONG RecvSize)
}
int start(PWSTR PipeBuf, ULONG PipeBufSize,
- PWSTR ClassName, PWSTR InstanceName, DWORD Argc, PWSTR *Argv)
+ PWSTR ClassName, PWSTR InstanceName, DWORD Argc, PWSTR *Argv,
+ BOOLEAN HasSecret)
{
PWSTR P;
DWORD ClassNameSize, InstanceNameSize, ArgvSize;
@@ -130,7 +131,7 @@ int start(PWSTR PipeBuf, ULONG PipeBufSize,
return ERROR_INVALID_PARAMETER;
P = PipeBuf;
- *P++ = LauncherSvcInstanceStart;
+ *P++ = HasSecret ? LauncherSvcInstanceStartWithSecret : LauncherSvcInstanceStart;
memcpy(P, ClassName, ClassNameSize * sizeof(WCHAR)); P += ClassNameSize;
memcpy(P, InstanceName, InstanceNameSize * sizeof(WCHAR)); P += InstanceNameSize;
for (DWORD Argi = 0; Argc > Argi; Argi++)
@@ -230,7 +231,17 @@ int wmain(int argc, wchar_t **argv)
if (3 > argc || argc > 12)
usage();
- return start(PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE, argv[1], argv[2], argc - 3, argv + 3);
+ return start(PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE, argv[1], argv[2], argc - 3, argv + 3,
+ FALSE);
+ }
+ else
+ if (0 == lstrcmpW(L"startWithSecret", argv[0]))
+ {
+ if (3 > argc || argc > 12)
+ usage();
+
+ return start(PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE, argv[1], argv[2], argc - 3, argv + 3,
+ TRUE);
}
else
if (0 == lstrcmpW(L"stop", argv[0]))
diff --git a/src/launcher/launcher.c b/src/launcher/launcher.c
index 398c206d..8383167d 100644
--- a/src/launcher/launcher.c
+++ b/src/launcher/launcher.c
@@ -21,6 +21,53 @@
#define PROGNAME "WinFsp.Launcher"
#define REGKEY "SYSTEM\\CurrentControlSet\\Services\\" PROGNAME "\\Services"
+BOOL CreateOverlappedPipe(
+ PHANDLE PReadPipe, PHANDLE PWritePipe, PSECURITY_ATTRIBUTES SecurityAttributes, DWORD Size,
+ DWORD ReadMode, DWORD WriteMode)
+{
+ RPC_STATUS RpcStatus;
+ UUID Uuid;
+ WCHAR PipeNameBuf[MAX_PATH];
+ HANDLE ReadPipe, WritePipe;
+ DWORD LastError;
+
+ RpcStatus = UuidCreate(&Uuid);
+ if (S_OK != RpcStatus && RPC_S_UUID_LOCAL_ONLY != RpcStatus)
+ {
+ SetLastError(ERROR_INTERNAL_ERROR);
+ return FALSE;
+ }
+
+ wsprintfW(PipeNameBuf, L"\\\\.\\pipe\\"
+ "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
+ Uuid.Data1, Uuid.Data2, Uuid.Data3,
+ Uuid.Data4[0], Uuid.Data4[1], Uuid.Data4[2], Uuid.Data4[3],
+ Uuid.Data4[4], Uuid.Data4[5], Uuid.Data4[6], Uuid.Data4[7]);
+
+ ReadPipe = CreateNamedPipeW(PipeNameBuf,
+ PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | ReadMode,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS,
+ 1, Size, Size, 120 * 1000, SecurityAttributes);
+ if (INVALID_HANDLE_VALUE == ReadPipe)
+ return FALSE;
+
+ WritePipe = CreateFileW(PipeNameBuf,
+ GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ SecurityAttributes, OPEN_EXISTING, WriteMode, 0);
+ if (INVALID_HANDLE_VALUE == WritePipe)
+ {
+ LastError = GetLastError();
+ CloseHandle(ReadPipe);
+ SetLastError(LastError);
+ return FALSE;
+ }
+
+ *PReadPipe = ReadPipe;
+ *PWritePipe = WritePipe;
+
+ return TRUE;
+}
+
typedef struct
{
HANDLE Process;
@@ -80,6 +127,7 @@ static VOID CALLBACK KillProcessWait(PVOID Context, BOOLEAN Timeout)
typedef struct
{
+ LONG RefCount;
PWSTR ClassName;
PWSTR InstanceName;
PWSTR CommandLine;
@@ -87,6 +135,7 @@ typedef struct
DWORD ProcessId;
HANDLE Process;
HANDLE ProcessWait;
+ HANDLE StdioHandles[2];
LIST_ENTRY ListEntry;
WCHAR Buffer[];
} SVC_INSTANCE;
@@ -231,8 +280,129 @@ static NTSTATUS SvcInstanceAccessCheck(HANDLE ClientToken, ULONG DesiredAccess,
return Result;
}
+NTSTATUS SvcInstanceCreateProcess(PWSTR Executable, PWSTR CommandLine,
+ HANDLE StdioHandles[2],
+ PPROCESS_INFORMATION ProcessInfo)
+{
+ STARTUPINFOEXW StartupInfoEx;
+ HANDLE ChildHandles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
+ HANDLE ParentHandles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
+ SECURITY_ATTRIBUTES PipeAttributes = { sizeof(SECURITY_ATTRIBUTES), 0, TRUE };
+ PPROC_THREAD_ATTRIBUTE_LIST AttrList = 0;
+ SIZE_T Size;
+ NTSTATUS Result;
+
+ memset(&StartupInfoEx, 0, sizeof StartupInfoEx);
+ StartupInfoEx.StartupInfo.cb = sizeof StartupInfoEx.StartupInfo;
+
+ if (0 != StdioHandles)
+ {
+ /*
+ * Create child process and redirect stdin/stdout. Do *not* inherit other handles.
+ *
+ * For explanation see:
+ * https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873/
+ */
+
+ /* create stdin read/write ends; make them inheritable */
+ if (!CreateOverlappedPipe(&ChildHandles[0], &ParentHandles[0], &PipeAttributes, 0,
+ 0, 0))
+ {
+ Result = FspNtStatusFromWin32(GetLastError());
+ goto exit;
+ }
+
+ /* create stdout read/write ends; make them inheritable */
+ if (!CreateOverlappedPipe(&ParentHandles[1], &ChildHandles[1], &PipeAttributes, 0,
+ FILE_FLAG_OVERLAPPED, 0))
+ {
+ Result = FspNtStatusFromWin32(GetLastError());
+ goto exit;
+ }
+
+ Size = 0;
+ if (!InitializeProcThreadAttributeList(0, 1, 0, &Size) &&
+ ERROR_INSUFFICIENT_BUFFER != GetLastError())
+ {
+ Result = FspNtStatusFromWin32(GetLastError());
+ goto exit;
+ }
+
+ AttrList = MemAlloc(Size);
+ if (0 == AttrList)
+ {
+ Result = STATUS_INSUFFICIENT_RESOURCES;
+ goto exit;
+ }
+
+ if (!InitializeProcThreadAttributeList(AttrList, 1, 0, &Size))
+ {
+ Result = FspNtStatusFromWin32(GetLastError());
+ goto exit;
+ }
+
+ /* only the child ends of stdin/stdout are actually inherited */
+ if (!UpdateProcThreadAttribute(AttrList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ ChildHandles, sizeof ChildHandles, 0, 0))
+ {
+ Result = FspNtStatusFromWin32(GetLastError());
+ goto exit;
+ }
+
+ StartupInfoEx.lpAttributeList = AttrList;
+ StartupInfoEx.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
+ StartupInfoEx.StartupInfo.hStdInput = ChildHandles[0];
+ StartupInfoEx.StartupInfo.hStdOutput = ChildHandles[1];
+ StartupInfoEx.StartupInfo.hStdError = INVALID_HANDLE_VALUE;
+
+ if (!CreateProcessW(Executable, CommandLine, 0, 0, TRUE,
+ CREATE_SUSPENDED | CREATE_NEW_PROCESS_GROUP | EXTENDED_STARTUPINFO_PRESENT, 0, 0,
+ (PVOID)&StartupInfoEx, ProcessInfo))
+ {
+ Result = FspNtStatusFromWin32(GetLastError());
+ goto exit;
+ }
+ }
+ else
+ {
+ if (!CreateProcessW(Executable, CommandLine, 0, 0, FALSE,
+ CREATE_SUSPENDED | CREATE_NEW_PROCESS_GROUP, 0, 0,
+ &StartupInfoEx.StartupInfo, ProcessInfo))
+ {
+ Result = FspNtStatusFromWin32(GetLastError());
+ goto exit;
+ }
+ }
+
+ Result = STATUS_SUCCESS;
+
+exit:
+ if (!NT_SUCCESS(Result))
+ {
+ if (INVALID_HANDLE_VALUE != ParentHandles[0])
+ CloseHandle(ParentHandles[0]);
+ if (INVALID_HANDLE_VALUE != ParentHandles[0])
+ CloseHandle(ParentHandles[0]);
+ }
+ else if (0 != StdioHandles)
+ {
+ StdioHandles[0] = ParentHandles[0];
+ StdioHandles[1] = ParentHandles[1];
+ }
+
+ if (INVALID_HANDLE_VALUE != ChildHandles[0])
+ CloseHandle(ChildHandles[0]);
+ if (INVALID_HANDLE_VALUE != ChildHandles[1])
+ CloseHandle(ChildHandles[1]);
+
+ MemFree(AttrList);
+
+ return Result;
+}
+
NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv0, HANDLE Job,
+ BOOLEAN RedirectStdio,
SVC_INSTANCE **PSvcInstance)
{
SVC_INSTANCE *SvcInstance = 0;
@@ -244,7 +414,6 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
DWORD JobControl;
PSECURITY_DESCRIPTOR SecurityDescriptor = 0;
PWSTR Argv[10];
- STARTUPINFOW StartupInfo;
PROCESS_INFORMATION ProcessInfo;
NTSTATUS Result;
@@ -259,7 +428,6 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
Argv[0] = 0;
Argc++;
- memset(&StartupInfo, 0, sizeof StartupInfo);
memset(&ProcessInfo, 0, sizeof ProcessInfo);
EnterCriticalSection(&SvcInstanceLock);
@@ -353,23 +521,23 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
}
memset(SvcInstance, 0, sizeof *SvcInstance);
+ SvcInstance->RefCount = 2;
memcpy(SvcInstance->Buffer, ClassName, ClassNameSize);
memcpy(SvcInstance->Buffer + ClassNameSize / sizeof(WCHAR), InstanceName, InstanceNameSize);
SvcInstance->ClassName = SvcInstance->Buffer;
SvcInstance->InstanceName = SvcInstance->Buffer + ClassNameSize / sizeof(WCHAR);
SvcInstance->SecurityDescriptor = SecurityDescriptor;
+ SvcInstance->StdioHandles[0] = INVALID_HANDLE_VALUE;
+ SvcInstance->StdioHandles[1] = INVALID_HANDLE_VALUE;
Result = SvcInstanceReplaceArguments(CommandLine, Argc, Argv, &SvcInstance->CommandLine);
if (!NT_SUCCESS(Result))
goto exit;
- StartupInfo.cb = sizeof StartupInfo;
- if (!CreateProcessW(Executable, SvcInstance->CommandLine, 0, 0, FALSE,
- CREATE_SUSPENDED | CREATE_NEW_PROCESS_GROUP, 0, 0, &StartupInfo, &ProcessInfo))
- {
- Result = FspNtStatusFromWin32(GetLastError());
+ Result = SvcInstanceCreateProcess(Executable, CommandLine,
+ RedirectStdio ? SvcInstance->StdioHandles : 0, &ProcessInfo);
+ if (!NT_SUCCESS(Result))
goto exit;
- }
SvcInstance->ProcessId = ProcessInfo.dwProcessId;
SvcInstance->Process = ProcessInfo.hProcess;
@@ -413,6 +581,11 @@ exit:
if (0 != SvcInstance)
{
+ if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[0])
+ CloseHandle(SvcInstance->StdioHandles[0]);
+ if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[1])
+ CloseHandle(SvcInstance->StdioHandles[1]);
+
if (0 != SvcInstance->ProcessWait)
UnregisterWaitEx(SvcInstance->ProcessWait, 0);
@@ -435,13 +608,21 @@ exit:
return Result;
}
-VOID SvcInstanceDelete(SVC_INSTANCE *SvcInstance)
+static VOID SvcInstanceRelease(SVC_INSTANCE *SvcInstance)
{
+ if (0 != InterlockedDecrement(&SvcInstance->RefCount))
+ return;
+
EnterCriticalSection(&SvcInstanceLock);
if (RemoveEntryList(&SvcInstance->ListEntry))
SetEvent(SvcInstanceEvent);
LeaveCriticalSection(&SvcInstanceLock);
+ if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[0])
+ CloseHandle(SvcInstance->StdioHandles[0]);
+ if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[1])
+ CloseHandle(SvcInstance->StdioHandles[1]);
+
if (0 != SvcInstance->ProcessWait)
UnregisterWaitEx(SvcInstance->ProcessWait, 0);
if (0 != SvcInstance->Process)
@@ -457,15 +638,79 @@ static VOID CALLBACK SvcInstanceTerminated(PVOID Context, BOOLEAN Timeout)
{
SVC_INSTANCE *SvcInstance = Context;
- SvcInstanceDelete(SvcInstance);
+ SvcInstanceRelease(SvcInstance);
}
NTSTATUS SvcInstanceStart(HANDLE ClientToken,
- PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, HANDLE Job)
+ PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, HANDLE Job,
+ BOOLEAN HasSecret)
{
SVC_INSTANCE *SvcInstance;
+ NTSTATUS Result;
- return SvcInstanceCreate(ClientToken, ClassName, InstanceName, Argc, Argv, Job, &SvcInstance);
+ HasSecret = HasSecret && 0 < Argc && L'\0' != Argv[Argc - 1][0];
+
+ Result = SvcInstanceCreate(ClientToken, ClassName, InstanceName,
+ Argc - HasSecret, Argv, Job, HasSecret,
+ &SvcInstance);
+ if (!NT_SUCCESS(Result))
+ return Result;
+
+ if (!HasSecret)
+ Result = STATUS_SUCCESS;
+ else
+ {
+ PWSTR Secret = Argv[Argc - 1];
+ UINT8 RspBuf[2];
+ DWORD BytesTransferred;
+ OVERLAPPED Overlapped;
+
+ if (!WriteFile(SvcInstance->StdioHandles[0], Secret, lstrlenW(Secret), &BytesTransferred, 0))
+ {
+ Result = FspNtStatusFromWin32(GetLastError());
+ goto exit;
+ }
+
+ memset(&Overlapped, 0, sizeof Overlapped);
+ if (!ReadFile(SvcInstance->StdioHandles[1], RspBuf, sizeof RspBuf, 0, &Overlapped) &&
+ ERROR_IO_PENDING != GetLastError())
+ {
+ Result = FspNtStatusFromWin32(GetLastError());
+ goto exit;
+ }
+
+ if (!GetOverlappedResultEx(SvcInstance->StdioHandles[1], &Overlapped, &BytesTransferred,
+ LAUNCHER_START_WITH_SECRET_TIMEOUT, FALSE))
+ {
+ if (WAIT_TIMEOUT == GetLastError())
+ Result = STATUS_TIMEOUT;
+ else
+ Result = FspNtStatusFromWin32(GetLastError());
+ CancelIoEx(SvcInstance->StdioHandles[1], &Overlapped);
+ goto exit;
+ }
+
+ if (sizeof RspBuf <= BytesTransferred && 'O' == RspBuf[0] && 'K' == RspBuf[1])
+ Result = STATUS_SUCCESS;
+ else
+ Result = STATUS_ACCESS_DENIED;
+ }
+
+exit:
+ if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[0])
+ {
+ CloseHandle(SvcInstance->StdioHandles[0]);
+ SvcInstance->StdioHandles[0] = INVALID_HANDLE_VALUE;
+ }
+ if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[1])
+ {
+ CloseHandle(SvcInstance->StdioHandles[1]);
+ SvcInstance->StdioHandles[1] = INVALID_HANDLE_VALUE;
+ }
+
+ SvcInstanceRelease(SvcInstance);
+
+ return Result;
}
NTSTATUS SvcInstanceStop(HANDLE ClientToken,
@@ -909,12 +1154,16 @@ static VOID SvcPipeTransact(HANDLE ClientToken, PWSTR PipeBuf, PULONG PSize)
PWSTR P = PipeBuf, PipeBufEnd = PipeBuf + *PSize / sizeof(WCHAR);
PWSTR ClassName, InstanceName;
ULONG Argc; PWSTR Argv[9];
+ BOOLEAN HasSecret = FALSE;
NTSTATUS Result;
*PSize = 0;
switch (*P++)
{
+ case LauncherSvcInstanceStartWithSecret:
+ HasSecret = TRUE;
+ /* fall through! */
case LauncherSvcInstanceStart:
ClassName = SvcPipeTransactGetPart(&P, PipeBufEnd);
InstanceName = SvcPipeTransactGetPart(&P, PipeBufEnd);
@@ -924,7 +1173,8 @@ static VOID SvcPipeTransact(HANDLE ClientToken, PWSTR PipeBuf, PULONG PSize)
Result = STATUS_INVALID_PARAMETER;
if (0 != ClassName && 0 != InstanceName)
- Result = SvcInstanceStart(ClientToken, ClassName, InstanceName, Argc, Argv, SvcJob);
+ Result = SvcInstanceStart(ClientToken, ClassName, InstanceName, Argc, Argv, SvcJob,
+ HasSecret);
SvcPipeTransactResult(Result, PipeBuf, PSize);
break;
diff --git a/src/launcher/launcher.h b/src/launcher/launcher.h
index d71f73b0..f1645b15 100644
--- a/src/launcher/launcher.h
+++ b/src/launcher/launcher.h
@@ -28,7 +28,9 @@
#define LAUNCHER_PIPE_BUFFER_SIZE 2048
#define LAUNCHER_PIPE_DEFAULT_TIMEOUT 3000
-/*
+#define LAUNCHER_START_WITH_SECRET_TIMEOUT 30000
+
+ /*
* The launcher named pipe SDDL gives full access to LocalSystem and Administrators and
* GENERIC_READ and FILE_WRITE_DATA access to Everyone. We are careful not to give the
* FILE_CREATE_PIPE_INSTANCE right to Everyone to disallow the creation of additional
@@ -53,6 +55,7 @@
enum
{
LauncherSvcInstanceStart = 'S', /* requires: SERVICE_START */
+ LauncherSvcInstanceStartWithSecret = 'X', /* requires: SERVICE_START */
LauncherSvcInstanceStop = 'T', /* requires: SERVICE_STOP */
LauncherSvcInstanceInfo = 'I', /* requires: SERVICE_QUERY_STATUS */
LauncherSvcInstanceList = 'L', /* requires: none*/