text: 
  /******************************************************************************\
  *       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)
  