1256 lines
36 KiB
C
1256 lines
36 KiB
C
|
|
/******************************************************************************\
|
|
* This is a part of the Microsoft Source Code Samples.
|
|
* Copyright 1995 - 1997 Microsoft Corporation.
|
|
* All rights reserved.
|
|
* This source code is only intended as a supplement to
|
|
* Microsoft Development Tools and/or WinHelp documentation.
|
|
* See these sources for detailed information regarding the
|
|
* Microsoft samples programs.
|
|
\******************************************************************************/
|
|
|
|
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
SrvUtil.c
|
|
|
|
Abstract:
|
|
|
|
The server component of Remote. It spawns a child process
|
|
and redirects the stdin/stdout/stderr of child to itself.
|
|
Waits for connections from clients - passing the
|
|
output of child process to client and the input from clients
|
|
to child process.
|
|
|
|
Author:
|
|
|
|
Rajivendra Nath 2-Jan-1992
|
|
Dave Hart 30 May 1997 split from Server.c
|
|
|
|
Environment:
|
|
|
|
Console App. User mode.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <precomp.h>
|
|
#include "Remote.h"
|
|
#include "Server.h"
|
|
#include <sddl.h>
|
|
#define DEFAULT_SECURITY_DESCRIPTOR L"D:(A;;FA;;;BA)(A;;FA;;;CO)(A;;0x1301bf;;;WD)"
|
|
#define REGISTRY_PATH L"Software\\Microsoft\\Remote"
|
|
#define REGISTRY_VALUE L"DefaultSecurity"
|
|
|
|
#define COMMANDFORMAT "%c%-20s [%-12s %s]\n%08x%c"
|
|
#define CMDSTRING(OutBuff,OutSize,InpBuff,Client,szTime,ForceShow) \
|
|
{ \
|
|
char *pch; \
|
|
\
|
|
for (pch = InpBuff; \
|
|
*pch; \
|
|
pch++) { \
|
|
\
|
|
if (ENDMARK == *pch || \
|
|
BEGINMARK == *pch) { \
|
|
\
|
|
*pch = '`'; \
|
|
} \
|
|
} \
|
|
\
|
|
OutSize = \
|
|
sprintf( \
|
|
(OutBuff), \
|
|
COMMANDFORMAT, \
|
|
BEGINMARK, \
|
|
(InpBuff), \
|
|
(Client)->Name, \
|
|
(szTime), \
|
|
(ForceShow) ? 0 : (Client)->dwID, \
|
|
ENDMARK \
|
|
); \
|
|
}
|
|
|
|
|
|
/*************************************************************/
|
|
// GetFormattedTime -- returns pointer to formatted time
|
|
//
|
|
// returns pointer to static buffer, only the main thread
|
|
// should use this.
|
|
//
|
|
|
|
PCHAR
|
|
GetFormattedTime(
|
|
BOOL bDateToo
|
|
)
|
|
{
|
|
static char szTime[64];
|
|
int cch = 0;
|
|
|
|
if (bDateToo) {
|
|
|
|
cch =
|
|
GetDateFormat(
|
|
LOCALE_USER_DEFAULT,
|
|
0,
|
|
NULL, // current date
|
|
"ddd", // short day of week
|
|
szTime,
|
|
sizeof szTime
|
|
);
|
|
|
|
// cch includes null terminator, change it to
|
|
// a space to separate from time.
|
|
|
|
szTime[ cch - 1 ] = ' ';
|
|
}
|
|
|
|
//
|
|
// Get time and format to characters
|
|
//
|
|
|
|
GetTimeFormat(
|
|
LOCALE_USER_DEFAULT,
|
|
TIME_NOSECONDS,
|
|
NULL, // use current time
|
|
NULL, // use default format
|
|
szTime + cch,
|
|
(sizeof szTime) - cch );
|
|
|
|
return szTime;
|
|
}
|
|
|
|
/*************************************************************/
|
|
|
|
BOOL
|
|
FilterCommand(
|
|
REMOTE_CLIENT *cl,
|
|
char *buff,
|
|
int dread
|
|
)
|
|
{
|
|
char tmpchar;
|
|
DWORD tmp;
|
|
int len, i;
|
|
DWORD ThreadID;
|
|
char inp_buff[2048];
|
|
char ch[3];
|
|
|
|
if (dread==0)
|
|
return(FALSE);
|
|
|
|
buff[dread]=0;
|
|
|
|
if (buff[0]==COMMANDCHAR)
|
|
{
|
|
|
|
switch(buff[1]) {
|
|
case 'k':
|
|
case 'K':
|
|
|
|
if (INVALID_HANDLE_VALUE != hWriteChildStdIn) {
|
|
|
|
printf("Remote: killing child softly, @K again to be more convincing.\n");
|
|
|
|
CancelIo( hWriteChildStdIn );
|
|
CloseHandle( hWriteChildStdIn );
|
|
hWriteChildStdIn = INVALID_HANDLE_VALUE;
|
|
|
|
GenerateConsoleCtrlEvent(CTRL_CLOSE_EVENT, 0);
|
|
SleepEx(200, TRUE);
|
|
cPendingCtrlCEvents++;
|
|
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
|
|
SleepEx(20, TRUE);
|
|
GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0);
|
|
|
|
} else {
|
|
|
|
printf("Remote: Resorting to TerminateProcess.\n");
|
|
|
|
TerminateProcess(ChldProc, ERROR_PROCESS_ABORTED);
|
|
}
|
|
|
|
|
|
break;
|
|
case 's':
|
|
case 'S':
|
|
CloseHandle( (HANDLE)
|
|
_beginthreadex(
|
|
NULL, // security
|
|
0, // default stack size
|
|
SendStatus,
|
|
(void *) cl->PipeWriteH,
|
|
0, // not suspended
|
|
&ThreadID
|
|
));
|
|
break;
|
|
|
|
case 'p':
|
|
case 'P':
|
|
{
|
|
char *msg;
|
|
|
|
msg = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 4096 );
|
|
if ( ! msg) {
|
|
break;
|
|
}
|
|
|
|
sprintf(msg,"From %s %s [%s]\n\n%s\n",cl->Name,cl->UserName,GetFormattedTime(TRUE),&buff[2]);
|
|
|
|
if (WriteFileSynch(hWriteTempFile,msg,strlen(msg),&tmp,dwWriteFilePointer,&olMainThread)) {
|
|
dwWriteFilePointer += tmp;
|
|
StartServerToClientFlow();
|
|
}
|
|
|
|
CloseHandle( (HANDLE)
|
|
CreateThread( // no CRT for ShowPopup
|
|
NULL, // security
|
|
0, // default stack size
|
|
ShowPopup,
|
|
(void *) msg,
|
|
0, // not suspended
|
|
&ThreadID
|
|
));
|
|
|
|
break;
|
|
}
|
|
|
|
case 'm':
|
|
case 'M':
|
|
buff[dread-2]=0;
|
|
CMDSTRING(inp_buff,len,buff,cl,GetFormattedTime(TRUE),TRUE);
|
|
|
|
if (WriteFileSynch(hWriteTempFile,inp_buff,len,&tmp,dwWriteFilePointer,&olMainThread)) {
|
|
dwWriteFilePointer += tmp;
|
|
StartServerToClientFlow();
|
|
}
|
|
break;
|
|
|
|
case '@':
|
|
buff[dread-2]=0;
|
|
CMDSTRING(inp_buff,len,&buff[1],cl,GetFormattedTime(FALSE),FALSE);
|
|
if (WriteFileSynch(hWriteTempFile,inp_buff,len,&tmp,dwWriteFilePointer,&olMainThread)) {
|
|
dwWriteFilePointer += tmp;
|
|
StartServerToClientFlow();
|
|
}
|
|
//
|
|
// Remove the first @ sign
|
|
//
|
|
MoveMemory(buff,&buff[1],dread-1);
|
|
buff[dread-1]=' ';
|
|
return(FALSE); //Send it it to the chile process
|
|
|
|
|
|
default :
|
|
ZeroMemory(inp_buff, sizeof(inp_buff));
|
|
strncpy(inp_buff, "** Unknown Command **\n", sizeof(inp_buff)-1);
|
|
if (WriteFileSynch(hWriteTempFile,inp_buff,strlen(inp_buff),&tmp,dwWriteFilePointer,&olMainThread)) {
|
|
dwWriteFilePointer += tmp;
|
|
// we do this below // StartServerToClientFlow();
|
|
}
|
|
case 'h':
|
|
case 'H':
|
|
_snprintf(inp_buff,sizeof(inp_buff), "%cM: To Send Message\n",COMMANDCHAR);
|
|
if (WriteFileSynch(hWriteTempFile,inp_buff,strlen(inp_buff),&tmp,dwWriteFilePointer,&olMainThread)) {
|
|
dwWriteFilePointer += tmp;
|
|
}
|
|
_snprintf(inp_buff,sizeof(inp_buff), "%cP: To Generate popup\n",COMMANDCHAR);
|
|
if (WriteFileSynch(hWriteTempFile,inp_buff,strlen(inp_buff),&tmp,dwWriteFilePointer,&olMainThread)) {
|
|
dwWriteFilePointer += tmp;
|
|
}
|
|
_snprintf(inp_buff,sizeof(inp_buff), "%cK: To kill the server\n",COMMANDCHAR);
|
|
if (WriteFileSynch(hWriteTempFile,inp_buff,strlen(inp_buff),&tmp,dwWriteFilePointer,&olMainThread)) {
|
|
dwWriteFilePointer += tmp;
|
|
}
|
|
_snprintf(inp_buff,sizeof(inp_buff), "%cQ: To Quit client\n",COMMANDCHAR);
|
|
if (WriteFileSynch(hWriteTempFile,inp_buff,strlen(inp_buff),&tmp,dwWriteFilePointer,&olMainThread)) {
|
|
dwWriteFilePointer += tmp;
|
|
}
|
|
_snprintf(inp_buff,sizeof(inp_buff), "%cH: This Help\n",COMMANDCHAR);
|
|
if (WriteFileSynch(hWriteTempFile,inp_buff,strlen(inp_buff),&tmp,dwWriteFilePointer,&olMainThread)) {
|
|
dwWriteFilePointer += tmp;
|
|
}
|
|
StartServerToClientFlow();
|
|
break;
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
if ((buff[0]<26)) {
|
|
BOOL ret=FALSE;
|
|
|
|
_snprintf(ch, sizeof(ch), "^%c", buff[0] + 'A' - 1);
|
|
|
|
if (buff[0]==CTRLC) {
|
|
// show this even to this client
|
|
CMDSTRING(inp_buff,len,ch,cl,GetFormattedTime(FALSE),TRUE);
|
|
|
|
cPendingCtrlCEvents++;
|
|
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
|
|
ret = TRUE; // Already sent to child
|
|
} else {
|
|
CMDSTRING(inp_buff,len,ch,cl,GetFormattedTime(FALSE),FALSE);
|
|
}
|
|
|
|
if (WriteFileSynch(hWriteTempFile,inp_buff,len,&tmp,dwWriteFilePointer,&olMainThread)) {
|
|
dwWriteFilePointer += tmp;
|
|
StartServerToClientFlow();
|
|
}
|
|
return(ret); //FALSE:send it to child StdIn
|
|
}
|
|
|
|
// options here are CRLF(\r\n) or just LF(\n)
|
|
if (buff[dread-2] == 13) {
|
|
i = 2; // 13 is CR
|
|
} else {
|
|
i = 1;
|
|
}
|
|
|
|
tmpchar=buff[dread-i];
|
|
buff[dread-i]=0;
|
|
CMDSTRING(inp_buff,len,buff,cl,GetFormattedTime(FALSE),FALSE);
|
|
buff[dread-i]=tmpchar;
|
|
if (WriteFileSynch(hWriteTempFile,inp_buff,len,&tmp,dwWriteFilePointer,&olMainThread)) {
|
|
dwWriteFilePointer += tmp;
|
|
StartServerToClientFlow();
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
/*************************************************************/
|
|
HANDLE
|
|
ForkChildProcess( // Creates a new process
|
|
char *cmd, // Redirects its stdin,stdout
|
|
PHANDLE inH, // and stderr - returns the
|
|
PHANDLE outH // corresponding pipe ends.
|
|
)
|
|
{
|
|
SECURITY_ATTRIBUTES lsa;
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
HANDLE ChildIn;
|
|
HANDLE ChildOut, ChildOutDup;
|
|
HANDLE hWriteChild;
|
|
HANDLE hReadChild;
|
|
BOOL Success;
|
|
|
|
BOOL // pipeex.c
|
|
APIENTRY
|
|
MyCreatePipeEx(
|
|
OUT LPHANDLE lpReadPipe,
|
|
OUT LPHANDLE lpWritePipe,
|
|
IN LPSECURITY_ATTRIBUTES lpPipeAttributes,
|
|
IN DWORD nSize,
|
|
DWORD dwReadMode,
|
|
DWORD dwWriteMode
|
|
);
|
|
|
|
lsa.nLength=sizeof(SECURITY_ATTRIBUTES);
|
|
lsa.lpSecurityDescriptor=NULL;
|
|
lsa.bInheritHandle=TRUE;
|
|
|
|
//
|
|
// Create Parent_Write to ChildStdIn Pipe. Then
|
|
// duplicate the parent copy to a noninheritable
|
|
// handle and close the inheritable one so that
|
|
// the child won't be holding open a handle to
|
|
// the server end of its stdin pipe when we try
|
|
// to nuke that pipe to close the child.
|
|
//
|
|
|
|
Success = MyCreatePipeEx(
|
|
&ChildIn,
|
|
&hWriteChild,
|
|
&lsa,
|
|
0,
|
|
0,
|
|
FILE_FLAG_OVERLAPPED) &&
|
|
|
|
DuplicateHandle(
|
|
GetCurrentProcess(),
|
|
hWriteChild,
|
|
GetCurrentProcess(),
|
|
inH,
|
|
0, // ignored b/c SAME_ACCESS
|
|
FALSE, // not inheritable
|
|
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE
|
|
);
|
|
|
|
if (!Success) {
|
|
ErrorExit("Could Not Create Parent-->Child Pipe");
|
|
}
|
|
|
|
//
|
|
//Create ChildStdOut/stderr to Parent_Read pipe
|
|
//
|
|
|
|
Success = MyCreatePipeEx(
|
|
&hReadChild,
|
|
&ChildOut,
|
|
&lsa,
|
|
0,
|
|
FILE_FLAG_OVERLAPPED,
|
|
0) &&
|
|
|
|
DuplicateHandle(
|
|
GetCurrentProcess(),
|
|
hReadChild,
|
|
GetCurrentProcess(),
|
|
outH,
|
|
0, // ignored b/c SAME_ACCESS
|
|
FALSE, // not inheritable
|
|
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE
|
|
) &&
|
|
|
|
DuplicateHandle(
|
|
GetCurrentProcess(),
|
|
ChildOut,
|
|
GetCurrentProcess(),
|
|
&ChildOutDup,
|
|
0, // ignored b/c SAME_ACCESS
|
|
TRUE, // inheritable
|
|
DUPLICATE_SAME_ACCESS
|
|
);
|
|
|
|
if (!Success) {
|
|
ErrorExit("Could Not Create Child-->Parent Pipe");
|
|
}
|
|
|
|
ZeroMemory(&si, sizeof(si));
|
|
si.cb = sizeof(STARTUPINFO);
|
|
si.dwFlags = STARTF_USESTDHANDLES;
|
|
si.hStdInput = ChildIn;
|
|
si.hStdOutput = ChildOut;
|
|
si.hStdError = ChildOutDup;
|
|
si.wShowWindow = SW_SHOW;
|
|
|
|
//
|
|
// Create Child Process
|
|
//
|
|
|
|
if ( ! CreateProcess(
|
|
NULL,
|
|
cmd,
|
|
NULL,
|
|
NULL,
|
|
TRUE,
|
|
GetPriorityClass( GetCurrentProcess() ),
|
|
NULL,
|
|
NULL,
|
|
&si,
|
|
&pi)) {
|
|
|
|
if (GetLastError()==2) {
|
|
printf("Executable %s not found\n",cmd);
|
|
} else {
|
|
printf("CreateProcess(%s) failed, error %d.\n", cmd, GetLastError());
|
|
}
|
|
ErrorExit("Could Not Create Child Process");
|
|
}
|
|
|
|
//
|
|
// Close unneccesary Handles
|
|
//
|
|
|
|
CloseHandle(ChildIn);
|
|
CloseHandle(ChildOut);
|
|
CloseHandle(ChildOutDup);
|
|
CloseHandle(pi.hThread);
|
|
|
|
pidChild = pi.dwProcessId;
|
|
|
|
return(pi.hProcess);
|
|
}
|
|
|
|
//
|
|
// SendStatus runs as its own thread, with C runtime available.
|
|
//
|
|
|
|
DWORD
|
|
WINAPI
|
|
SendStatus(
|
|
LPVOID lpSendStatusParm
|
|
)
|
|
{
|
|
HANDLE hClientPipe = (HANDLE) lpSendStatusParm;
|
|
char *pch;
|
|
DWORD tmp;
|
|
PREMOTE_CLIENT pClient;
|
|
OVERLAPPED ol;
|
|
char buff[2048];
|
|
char szSep[] = " ------------------------------\n";
|
|
|
|
//
|
|
// Since we're in our own thread we need our own
|
|
// overlapped structure for our client pipe writes.
|
|
//
|
|
|
|
ZeroMemory(&ol, sizeof(ol));
|
|
|
|
ol.hEvent = CreateEvent(
|
|
NULL, // security
|
|
TRUE, // auto-reset
|
|
FALSE, // initially nonsignaled
|
|
NULL // unnamed
|
|
);
|
|
|
|
//
|
|
// Dump the closing client list
|
|
//
|
|
|
|
pch = buff;
|
|
|
|
EnterCriticalSection(&csClosingClientList);
|
|
|
|
for (pClient = (PREMOTE_CLIENT) ClosingClientListHead.Flink;
|
|
pClient != (PREMOTE_CLIENT) &ClosingClientListHead;
|
|
pClient = (PREMOTE_CLIENT) pClient->Links.Flink ) {
|
|
|
|
if (pch + 60 > buff + sizeof(buff)) {
|
|
|
|
break;
|
|
}
|
|
|
|
pch += sprintf(pch, "%d: %s %s (Disconnected)\n", pClient->dwID, pClient->Name, pClient->UserName);
|
|
}
|
|
|
|
LeaveCriticalSection(&csClosingClientList);
|
|
|
|
WriteFileSynch(hClientPipe, buff, (DWORD)(pch - buff), &tmp, 0, &ol);
|
|
|
|
WriteFileSynch(hClientPipe, szSep, sizeof(szSep) - 1, &tmp, 0, &ol);
|
|
|
|
//
|
|
// Dump the normal client list
|
|
//
|
|
|
|
pch = buff;
|
|
|
|
EnterCriticalSection(&csClientList);
|
|
|
|
for (pClient = (PREMOTE_CLIENT) ClientListHead.Flink;
|
|
pClient != (PREMOTE_CLIENT) &ClientListHead;
|
|
pClient = (PREMOTE_CLIENT) pClient->Links.Flink ) {
|
|
|
|
if (pch + 60 > buff + sizeof(buff)) {
|
|
|
|
break;
|
|
}
|
|
|
|
pch += sprintf(pch, "%d: %s %s\n", pClient->dwID, pClient->Name, pClient->UserName);
|
|
}
|
|
|
|
LeaveCriticalSection(&csClientList);
|
|
|
|
WriteFileSynch(hClientPipe, buff, (DWORD)(pch - buff), &tmp, 0, &ol);
|
|
|
|
WriteFileSynch(hClientPipe, szSep, sizeof(szSep) - 1, &tmp, 0, &ol);
|
|
|
|
//
|
|
// Dump the handshaking client list
|
|
//
|
|
|
|
pch = buff;
|
|
|
|
EnterCriticalSection(&csHandshakingList);
|
|
|
|
for (pClient = (PREMOTE_CLIENT) HandshakingListHead.Flink;
|
|
pClient != (PREMOTE_CLIENT) &HandshakingListHead;
|
|
pClient = (PREMOTE_CLIENT) pClient->Links.Flink ) {
|
|
|
|
if (pch + 60 > buff + sizeof(buff)) {
|
|
|
|
break;
|
|
}
|
|
|
|
pch += sprintf(pch, "%d: %s %s (Connecting)\n", pClient->dwID, pClient->Name, pClient->UserName);
|
|
}
|
|
|
|
LeaveCriticalSection(&csHandshakingList);
|
|
|
|
WriteFileSynch(hClientPipe, buff, (DWORD)(pch - buff), &tmp, 0, &ol);
|
|
|
|
WriteFileSynch(hClientPipe, szSep, sizeof(szSep) - 1, &tmp, 0, &ol);
|
|
|
|
//
|
|
// Dump summary information.
|
|
//
|
|
|
|
pch = buff;
|
|
|
|
pch += sprintf(pch, "REMOTE /C %s \"%s\"\n", HostName, PipeName);
|
|
pch += sprintf(pch, "Command: %s\n", ChildCmd);
|
|
pch += sprintf(pch, "Windows NT %d.%d build %d \n",
|
|
OsVersionInfo.dwMajorVersion,
|
|
OsVersionInfo.dwMinorVersion,
|
|
OsVersionInfo.dwBuildNumber);
|
|
|
|
WriteFileSynch(hClientPipe, buff, (DWORD)(pch - buff), &tmp, 0, &ol);
|
|
|
|
WriteFileSynch(hClientPipe, szSep, sizeof(szSep) - 1, &tmp, 0, &ol);
|
|
|
|
CloseHandle(ol.hEvent);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************/
|
|
|
|
DWORD // NO CRT for ShowPopup
|
|
WINAPI
|
|
ShowPopup(
|
|
void *vpArg
|
|
)
|
|
{
|
|
char *msg = (char *) vpArg;
|
|
|
|
MessageBox(GetActiveWindow(),msg,"** REMOTE.EXE **",MB_OK|MB_SETFOREGROUND);
|
|
HeapFree(hHeap, 0, msg);
|
|
return(0);
|
|
}
|
|
|
|
/*************************************************************/
|
|
|
|
//
|
|
// SrvCtrlHand is the console event handler for the server side
|
|
// of remote. If our stdin is a console handle, we've disabled
|
|
// generation of ^C events by the console code. Therefore
|
|
// any we see are either generated by us for the benefit of
|
|
// our child processes sharing the console, or generated by
|
|
// some other process. We want to ignore the ones we generate
|
|
// (since we're already done with everything that needs to be
|
|
// done at that point), and also ignore ^C's generated by
|
|
// other processes since we don't need to do anything with those.
|
|
// For example if someone runs:
|
|
//
|
|
// remote /s "remote /s cmd inner" outer
|
|
//
|
|
// Then local keyboard ^C's will be read by the outer remote.exe
|
|
// from its stdin handle, then it will generate a CTRL_C_EVENT that
|
|
// all processes in the console will see, including both remote.exe's
|
|
// and the child cmd.exe. So the handler needs do nothing but indicate
|
|
// the event was handled by returning TRUE so the default handler
|
|
// won't kill us. For ^BREAK we want to specifically kill our child
|
|
// process so that cmd.exe and others that ignore ^BREAK will go away.
|
|
// Of course this won't kill our grandchildren and so on. Oh well.
|
|
//
|
|
// For all other events we return FALSE and let the default handler
|
|
// have it.
|
|
//
|
|
|
|
BOOL
|
|
WINAPI
|
|
SrvCtrlHand(
|
|
DWORD event
|
|
)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
DWORD cb;
|
|
DWORD dwTempFileOffset;
|
|
OVERLAPPED ol;
|
|
char szTime[64];
|
|
char szCmd[128];
|
|
|
|
if (event == CTRL_BREAK_EVENT) {
|
|
TerminateProcess(ChldProc, 3);
|
|
bRet = TRUE;
|
|
} else if (event == CTRL_C_EVENT) {
|
|
if ( ! cPendingCtrlCEvents ) {
|
|
|
|
//
|
|
// This came from the local keyboard or
|
|
// was generated by another process in
|
|
// this console. Echo it as a local
|
|
// command. We have use GetTimeFormat
|
|
// here not our GetFormattedTime since
|
|
// the latter is for the use of the
|
|
// main thread only.
|
|
//
|
|
|
|
GetTimeFormat(
|
|
LOCALE_USER_DEFAULT,
|
|
TIME_NOSECONDS,
|
|
NULL, // use current time
|
|
NULL, // use default format
|
|
szTime,
|
|
sizeof(szTime)
|
|
);
|
|
|
|
CMDSTRING(szCmd, cb, "^C", pLocalClient, szTime, TRUE);
|
|
|
|
ZeroMemory(&ol, sizeof(ol));
|
|
ol.hEvent =
|
|
CreateEvent(
|
|
NULL, // security
|
|
TRUE, // auto-reset
|
|
FALSE, // initially nonsignaled
|
|
NULL // unnamed
|
|
);
|
|
|
|
//
|
|
// Practically all writes to the tempfile are happening on
|
|
// the primary server thread. We're on a Ctrl-C thread.
|
|
// We can't start the server to client I/O going after
|
|
// writing because we're on the wrong thread, so we
|
|
// punt. To fix this we need an event we can signal
|
|
// that causes the main thread to call StartServerToClientFlow.
|
|
//
|
|
|
|
dwTempFileOffset = dwWriteFilePointer;
|
|
dwWriteFilePointer += cb;
|
|
WriteFileSynch(hWriteTempFile, szCmd, cb, &cb, dwTempFileOffset, &ol);
|
|
// wrong thread // StartServerToClientFlow();
|
|
|
|
CloseHandle(ol.hEvent);
|
|
|
|
} else {
|
|
|
|
//
|
|
// We generated this event in response to a ^C received from
|
|
// a client, it's already been displayed to all clients.
|
|
//
|
|
|
|
cPendingCtrlCEvents--;
|
|
}
|
|
|
|
bRet = TRUE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*************************************************************/
|
|
|
|
typedef BOOL (STRINGSDTOSDW)(
|
|
LPWSTR String,
|
|
DWORD Version,
|
|
PSECURITY_DESCRIPTOR * pSD,
|
|
PULONG SDSize
|
|
);
|
|
typedef STRINGSDTOSDW * PSTRINGSDTOSDW ;
|
|
|
|
typedef BOOL (SDTOSTRINGSDW)(
|
|
PSECURITY_DESCRIPTOR SD,
|
|
DWORD StringVersion,
|
|
SECURITY_INFORMATION SecInfo,
|
|
LPWSTR * StringDescriptor,
|
|
PULONG Size
|
|
);
|
|
typedef SDTOSTRINGSDW * PSDTOSTRINGSDW ;
|
|
|
|
BOOL
|
|
SddlToSecurityDescriptor(
|
|
LPWSTR String,
|
|
DWORD Version,
|
|
PSECURITY_DESCRIPTOR * pSD,
|
|
PULONG SDSize
|
|
)
|
|
{
|
|
HMODULE hModule ;
|
|
PSTRINGSDTOSDW pStringSecurityDescriptorToSecurityDescriptor ;
|
|
BOOL Success = FALSE ;
|
|
PSECURITY_DESCRIPTOR sd ;
|
|
|
|
hModule = GetModuleHandle( "advapi32.dll" );
|
|
|
|
if ( hModule )
|
|
{
|
|
pStringSecurityDescriptorToSecurityDescriptor = (PSTRINGSDTOSDW) GetProcAddress(
|
|
hModule, "ConvertStringSecurityDescriptorToSecurityDescriptorW" );
|
|
|
|
if ( pStringSecurityDescriptorToSecurityDescriptor )
|
|
{
|
|
Success = pStringSecurityDescriptorToSecurityDescriptor( String, Version, pSD, SDSize);
|
|
return Success ;
|
|
}
|
|
}
|
|
|
|
sd = LocalAlloc( LMEM_FIXED, sizeof( SECURITY_DESCRIPTOR ) );
|
|
|
|
if ( sd )
|
|
{
|
|
InitializeSecurityDescriptor(
|
|
sd,
|
|
SECURITY_DESCRIPTOR_REVISION
|
|
);
|
|
|
|
SetSecurityDescriptorDacl(
|
|
sd,
|
|
TRUE,
|
|
NULL,
|
|
FALSE
|
|
);
|
|
|
|
*pSD = sd ;
|
|
if ( SDSize )
|
|
{
|
|
*SDSize = sizeof( SECURITY_DESCRIPTOR );
|
|
|
|
}
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
return FALSE ;
|
|
}
|
|
|
|
/*************************************************************/
|
|
|
|
BOOL
|
|
SDtoStringSD(
|
|
PSECURITY_DESCRIPTOR pSD,
|
|
DWORD Version,
|
|
SECURITY_INFORMATION SecInfo,
|
|
LPWSTR * StringSD,
|
|
PULONG StringSize
|
|
)
|
|
{
|
|
HMODULE hModule ;
|
|
PSDTOSTRINGSDW pSDtoStringSD ;
|
|
BOOL Success = FALSE ;
|
|
|
|
hModule = GetModuleHandle( "advapi32.dll" );
|
|
|
|
if ( hModule )
|
|
{
|
|
pSDtoStringSD = (PSDTOSTRINGSDW) GetProcAddress(
|
|
hModule, "ConvertSecurityDescriptorToStringSecurityDescriptorW" );
|
|
|
|
if ( pSDtoStringSD )
|
|
{
|
|
Success = pSDtoStringSD(pSD, Version, SecInfo, StringSD, StringSize );
|
|
|
|
FreeLibrary( hModule );
|
|
|
|
return Success ;
|
|
}
|
|
}
|
|
|
|
return FALSE ;
|
|
}
|
|
|
|
/*************************************************************/
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
FormatSecurityDescriptor(
|
|
CHAR * * DenyNames,
|
|
DWORD DenyCount,
|
|
CHAR * * Names,
|
|
DWORD Count)
|
|
{
|
|
PSECURITY_DESCRIPTOR Sd;
|
|
PACL Acl;
|
|
DWORD i;
|
|
PSID Sids;
|
|
DWORD SidLength ;
|
|
CHAR ReferencedDomain[ MAX_PATH ];
|
|
UCHAR SidBuffer[ 8 * sizeof(DWORD) + 8 ];
|
|
DWORD DomainLen ;
|
|
SID_NAME_USE Use;
|
|
DWORD SdLen;
|
|
|
|
SdLen = sizeof(SECURITY_DESCRIPTOR) +
|
|
DenyCount * (sizeof( ACCESS_DENIED_ACE ) ) +
|
|
DenyCount * GetSidLengthRequired( 8 ) +
|
|
Count * (sizeof( ACCESS_ALLOWED_ACE ) ) + sizeof(ACL) +
|
|
(Count * GetSidLengthRequired( 8 ) );
|
|
|
|
Sd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, SdLen );
|
|
if ( !Sd ) {
|
|
ErrorExit("Could not allocate SD");
|
|
}
|
|
|
|
InitializeSecurityDescriptor( Sd, SECURITY_DESCRIPTOR_REVISION );
|
|
|
|
Acl = (PACL)( (PUCHAR) Sd + sizeof( SECURITY_DESCRIPTOR) );
|
|
|
|
InitializeAcl( Acl, SdLen - sizeof( SECURITY_DESCRIPTOR) ,
|
|
ACL_REVISION );
|
|
|
|
Sids = SidBuffer;
|
|
for (i = 0 ; i < DenyCount ; i ++ ) {
|
|
SidLength = sizeof( SidBuffer );
|
|
|
|
DomainLen = MAX_PATH ;
|
|
|
|
if (! LookupAccountName(NULL,
|
|
DenyNames[ i ],
|
|
Sids,
|
|
&SidLength,
|
|
ReferencedDomain,
|
|
&DomainLen,
|
|
&Use ) )
|
|
{
|
|
_snprintf( ReferencedDomain, MAX_PATH, "Unable to find account %s", DenyNames[ i ]);
|
|
ErrorExit( ReferencedDomain );
|
|
}
|
|
|
|
//
|
|
// Got the sid. Now, add it as an access denied ace:
|
|
//
|
|
|
|
AddAccessDeniedAce( Acl,
|
|
ACL_REVISION,
|
|
FILE_GENERIC_READ |
|
|
FILE_GENERIC_WRITE |
|
|
FILE_CREATE_PIPE_INSTANCE,
|
|
Sids );
|
|
|
|
|
|
}
|
|
|
|
for (i = 0 ; i < Count ; i ++ ) {
|
|
SidLength = sizeof( SidBuffer );
|
|
|
|
DomainLen = MAX_PATH ;
|
|
|
|
if (! LookupAccountName(NULL,
|
|
Names[ i ],
|
|
Sids,
|
|
&SidLength,
|
|
ReferencedDomain,
|
|
&DomainLen,
|
|
&Use ) )
|
|
{
|
|
_snprintf( ReferencedDomain, MAX_PATH, "Unable to find account %s", Names[ i ]);
|
|
ErrorExit( ReferencedDomain );
|
|
}
|
|
|
|
//
|
|
// Got the sid. Now, add it as an access allowed ace:
|
|
//
|
|
|
|
AddAccessAllowedAce(Acl,
|
|
ACL_REVISION,
|
|
FILE_GENERIC_READ |
|
|
FILE_GENERIC_WRITE |
|
|
FILE_CREATE_PIPE_INSTANCE,
|
|
Sids );
|
|
}
|
|
|
|
//
|
|
// Now the ACL should be complete, so set it into the SD and return:
|
|
//
|
|
|
|
SetSecurityDescriptorDacl( Sd, TRUE, Acl, FALSE );
|
|
|
|
return Sd ;
|
|
}
|
|
|
|
|
|
/*************************************************************/
|
|
|
|
VOID
|
|
CloseClient(
|
|
REMOTE_CLIENT *pClient
|
|
)
|
|
{
|
|
DWORD tmp;
|
|
char Buf[200];
|
|
|
|
#if DBG
|
|
if (pClient->ServerFlags & ~SFLG_VALID) {
|
|
|
|
printf("pClient %p looks nasty in CloseClient.\n", pClient);
|
|
ErrorExit("REMOTE_CLIENT structure corrupt.");
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// If we're still active (on the normal client list)
|
|
// start tearing things down and move to the closing
|
|
// list.
|
|
//
|
|
|
|
if (pClient->ServerFlags & SFLG_CLOSING) {
|
|
return;
|
|
}
|
|
|
|
if (pClient->ServerFlags & SFLG_HANDSHAKING) {
|
|
MoveClientToNormalList(pClient);
|
|
}
|
|
|
|
MoveClientToClosingList(pClient);
|
|
|
|
pClient->ServerFlags |= SFLG_CLOSING;
|
|
|
|
|
|
if (pClient->PipeWriteH != INVALID_HANDLE_VALUE) {
|
|
TRACE(CONNECT, ("Disconnecting %d PipeWriteH (%p).\n", pClient->dwID, pClient->PipeWriteH));
|
|
CancelIo(pClient->PipeWriteH);
|
|
DisconnectNamedPipe(pClient->PipeWriteH);
|
|
CloseHandle(pClient->PipeWriteH);
|
|
}
|
|
|
|
if (pClient->PipeReadH != INVALID_HANDLE_VALUE &&
|
|
pClient->PipeReadH != pClient->PipeWriteH) {
|
|
|
|
TRACE(CONNECT, ("Disconnecting %d PipeReadH (%p).\n", pClient->dwID, pClient->PipeReadH));
|
|
CancelIo(pClient->PipeReadH);
|
|
DisconnectNamedPipe(pClient->PipeReadH);
|
|
CloseHandle(pClient->PipeReadH);
|
|
}
|
|
|
|
|
|
if (pClient->rSaveFile != INVALID_HANDLE_VALUE) {
|
|
CancelIo(pClient->rSaveFile);
|
|
CloseHandle(pClient->rSaveFile);
|
|
}
|
|
|
|
pClient->rSaveFile =
|
|
pClient->PipeWriteH =
|
|
pClient->PipeReadH =
|
|
INVALID_HANDLE_VALUE;
|
|
|
|
|
|
if ( ! bShuttingDownServer ) {
|
|
ZeroMemory(Buf, sizeof(Buf));
|
|
_snprintf(Buf, sizeof(Buf)-1, "\n**Remote: Disconnected from %s %s [%s]\n", pClient->Name, pClient->UserName, GetFormattedTime(TRUE));
|
|
|
|
if (WriteFileSynch(hWriteTempFile,Buf,strlen(Buf),&tmp,dwWriteFilePointer,&olMainThread)) {
|
|
dwWriteFilePointer += tmp;
|
|
StartServerToClientFlow();
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
FASTCALL
|
|
HandleSessionError(
|
|
PREMOTE_CLIENT pClient,
|
|
DWORD dwError
|
|
)
|
|
{
|
|
if (pClient->ServerFlags & SFLG_CLOSING) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (dwError) {
|
|
if (ERROR_BROKEN_PIPE == dwError ||
|
|
ERROR_OPERATION_ABORTED == dwError ||
|
|
ERROR_NO_DATA == dwError )
|
|
{
|
|
CloseClient(pClient);
|
|
return TRUE;
|
|
}
|
|
|
|
SetLastError(dwError);
|
|
ErrorExit("Unhandled session error.");
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
CleanupTempFiles(
|
|
PSZ pszTempDir
|
|
)
|
|
{
|
|
HANDLE hSearch;
|
|
WIN32_FIND_DATA FindData;
|
|
char szPath[MAX_PATH + 1] = {0};
|
|
char szFile[MAX_PATH + 1];
|
|
|
|
//
|
|
// pszTempDir, from GetTempPath, has a trailing backslash.
|
|
//
|
|
|
|
_snprintf(szPath, sizeof(szPath)-1, "%sREM*.tmp", pszTempDir);
|
|
|
|
hSearch = FindFirstFile(szPath, &FindData);
|
|
|
|
if (INVALID_HANDLE_VALUE != hSearch) {
|
|
do {
|
|
ZeroMemory(szFile, sizeof(szFile));
|
|
_snprintf(szFile, sizeof(szFile)-1, "%s%s", pszTempDir, FindData.cFileName);
|
|
|
|
DeleteFile(szFile);
|
|
} while (FindNextFile(hSearch, &FindData));
|
|
|
|
FindClose(hSearch);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
SaveDacl(
|
|
PSECURITY_DESCRIPTOR psd
|
|
)
|
|
{
|
|
HKEY hKey ;
|
|
int err ;
|
|
DWORD disp ;
|
|
LPWSTR StringSD ;
|
|
DWORD StringLen;
|
|
|
|
err = RegCreateKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
REGISTRY_PATH,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_WRITE,
|
|
NULL,
|
|
&hKey,
|
|
&disp );
|
|
|
|
if ( err == 0 ) {
|
|
if ( SDtoStringSD(sdPublic, 1, DACL_SECURITY_INFORMATION, &StringSD, &StringLen ) ) {
|
|
err = RegSetValueExW(
|
|
hKey,
|
|
REGISTRY_VALUE,
|
|
0,
|
|
REG_SZ,
|
|
(LPBYTE) StringSD,
|
|
StringLen * sizeof(WCHAR) );
|
|
}
|
|
|
|
RegCloseKey( hKey );
|
|
}
|
|
}
|
|
|
|
#pragma prefast(push)
|
|
#pragma prefast(disable: 248) // NULL dacl is by design here, really
|
|
VOID
|
|
FASTCALL
|
|
SetupSecurityDescriptors(
|
|
VOID
|
|
)
|
|
{
|
|
int i;
|
|
int err ;
|
|
HKEY hKey ;
|
|
PWSTR TextSD ;
|
|
DWORD Type ;
|
|
DWORD Size ;
|
|
PSID Everyone ;
|
|
PACL pDacl ;
|
|
SID_IDENTIFIER_AUTHORITY World = SECURITY_WORLD_SID_AUTHORITY ;
|
|
PSECURITY_DESCRIPTOR psd ;
|
|
|
|
pDacl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 12 + sizeof( ACCESS_ALLOWED_ACE ) + sizeof( ACL ));
|
|
psd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( SECURITY_DESCRIPTOR ));
|
|
|
|
//
|
|
// initialize the struct
|
|
//
|
|
saLocalNamedObjects.nLength = sizeof( SECURITY_ATTRIBUTES );
|
|
|
|
if ( (pDacl != NULL) && (psd != NULL ) ) {
|
|
if ( AllocateAndInitializeSid(&World, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &Everyone) ) {
|
|
InitializeAcl(pDacl, 12 + sizeof( ACCESS_ALLOWED_ACE ) + sizeof( ACL ), ACL_REVISION);
|
|
|
|
AddAccessAllowedAce(pDacl, ACL_REVISION,
|
|
EVENT_ALL_ACCESS | MUTEX_ALL_ACCESS | SYNCHRONIZE, Everyone );
|
|
|
|
InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION );
|
|
|
|
SetSecurityDescriptorDacl( psd, TRUE, pDacl, FALSE );
|
|
|
|
saLocalNamedObjects.bInheritHandle = FALSE ;
|
|
saLocalNamedObjects.lpSecurityDescriptor = psd ;
|
|
|
|
HeapFree(GetProcessHeap(), 0, Everyone );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize the wide-open security descriptor.
|
|
//
|
|
|
|
if ( !SddlToSecurityDescriptor(DEFAULT_SECURITY_DESCRIPTOR, 1, &sdPublic, NULL ) ) {
|
|
sdPublic = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( SECURITY_DESCRIPTOR ) );
|
|
|
|
if ( sdPublic ) {
|
|
InitializeSecurityDescriptor( sdPublic, SECURITY_DESCRIPTOR_REVISION );
|
|
SetSecurityDescriptorDacl( sdPublic, TRUE, NULL, FALSE );
|
|
}
|
|
}
|
|
|
|
saPublic.nLength = sizeof(saPublic);
|
|
saPublic.lpSecurityDescriptor = sdPublic;
|
|
|
|
//
|
|
// if /u was specified once or more, build the security descriptor to
|
|
// enforce it.
|
|
//
|
|
|
|
saPipe.nLength = sizeof(saPipe);
|
|
|
|
if ( DaclNameCount || DaclDenyNameCount ) {
|
|
saPipe.lpSecurityDescriptor =
|
|
FormatSecurityDescriptor( DaclDenyNames, DaclDenyNameCount, DaclNames, DaclNameCount );
|
|
|
|
if ( SaveDaclToRegistry ) {
|
|
SaveDacl( saPipe.lpSecurityDescriptor );
|
|
}
|
|
|
|
if (DaclNameCount) {
|
|
fputs( "\nProtected Server! Only the following users or groups can connect:\n", stdout );
|
|
|
|
for (i = 0 ; i < (int) DaclNameCount ; i++) {
|
|
printf( " %s\n", DaclNames[i] );
|
|
}
|
|
}
|
|
|
|
if (DaclDenyNameCount) {
|
|
fputs("The following users or groups explicitly cannot connect:\n", stdout );
|
|
|
|
for (i = 0 ; i < (int) DaclDenyNameCount ; i++) {
|
|
printf(" %s\n", DaclDenyNames[i] );
|
|
}
|
|
}
|
|
} else {
|
|
|
|
saPipe.lpSecurityDescriptor = sdPublic;
|
|
|
|
err = RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
REGISTRY_PATH,
|
|
0,
|
|
KEY_READ,
|
|
&hKey );
|
|
|
|
if ( err == 0 ) {
|
|
err = RegQueryValueExW(
|
|
hKey,
|
|
REGISTRY_VALUE,
|
|
0,
|
|
&Type,
|
|
NULL,
|
|
&Size );
|
|
|
|
if ( err != ERROR_FILE_NOT_FOUND ) {
|
|
TextSD = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Size );
|
|
|
|
if ( TextSD ) {
|
|
err = RegQueryValueExW(
|
|
hKey,
|
|
REGISTRY_VALUE,
|
|
0,
|
|
&Type,
|
|
(LPBYTE) TextSD,
|
|
&Size );
|
|
|
|
if ( err == 0 ) {
|
|
SddlToSecurityDescriptor(
|
|
TextSD, 1, &saPipe.lpSecurityDescriptor, NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
RegCloseKey( hKey );
|
|
}
|
|
}
|
|
}
|
|
#pragma prefast(pop)
|