Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1032 lines
27 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: session.c
  3. *
  4. * Copyright (c) 1991, Microsoft Corporation
  5. *
  6. * Remote shell session module
  7. *
  8. * History:
  9. * 06-28-92 Davidc Created.
  10. * 05-05-94 DaveTh Modifed for RCMD service
  11. * 02-04-99 MarkHar Fixed NTBug #287923
  12. \***************************************************************************/
  13. #include <nt.h>
  14. #include <ntrtl.h>
  15. #include <windef.h>
  16. #include <nturtl.h>
  17. #include <winbase.h>
  18. #include "rcmdsrv.h"
  19. #include <io.h>
  20. #include <stdlib.h>
  21. //
  22. // Define shell command line
  23. //
  24. #define SHELL_COMMAND_LINE TEXT("cmd /q")
  25. #define SHELL_COMMAND_PREFIX TEXT("cmd /c ")
  26. #define SHELL_REMOTE_CMD_TITLE TEXT("Remote Command")
  27. #define SHELL_CMD_PREFIX_LEN 7
  28. //
  29. // Define buffer size for reads/writes to/from shell
  30. //
  31. #define SHELL_BUFFER_SIZE 1000
  32. //
  33. // Home directory constants
  34. //
  35. #define MAX_DIRECTORY_LENGTH 255
  36. #define ROOT_OF_C TEXT("C:\\")
  37. //
  38. // Define multiple wait handle table size - extra handle for service stop
  39. //
  40. #define NUM_WAIT_HANDLES 5
  41. //
  42. // Define the structure used to describe each session
  43. //
  44. typedef struct {
  45. //
  46. // These fields are filled in at session creation time
  47. //
  48. HANDLE ShellReadPipeHandle; // Handle to shell stdout pipe
  49. HANDLE ShellWritePipeHandle; // Handle to shell stdin pipe
  50. HANDLE ShellProcessHandle; // Handle to shell process
  51. DWORD ShellProcessGroupId; // Process group ID of shell process
  52. PCHAR DefaultDirectory; // Default directory
  53. //
  54. // These fields maintain the state of asynchronouse reads/writes
  55. // to the shell process across client disconnections. They
  56. // are initialized at session creation.
  57. //
  58. BYTE ShellReadBuffer[SHELL_BUFFER_SIZE]; // Data for shell reads goes here
  59. HANDLE ShellReadAsyncHandle; // Object used for async reads from shell
  60. BYTE ShellWriteBuffer[SHELL_BUFFER_SIZE]; // Data for shell writes goes here
  61. HANDLE ShellWriteAsyncHandle; // Object used for async writes to shell
  62. //
  63. // These fields are filled in at session connect time and are only
  64. // valid when the session is connected
  65. //
  66. HANDLE ClientPipeHandle; // Handle to client pipe
  67. HANDLE SessionThreadHandle; // Handle to session thread
  68. } SESSION_DATA, *PSESSION_DATA;
  69. //
  70. // Private prototypes
  71. //
  72. HANDLE
  73. StartShell(
  74. HANDLE StdinPipeHandle,
  75. HANDLE StdoutPipeHandle,
  76. PSESSION_DATA Session,
  77. HANDLE TokenToUse,
  78. PCOMMAND_HEADER LpCommandHeader
  79. );
  80. DWORD
  81. SessionThreadFn(
  82. LPVOID Parameter
  83. );
  84. CHAR *
  85. GetDefaultDirectory(
  86. void
  87. );
  88. //
  89. // Useful macros
  90. //
  91. #define SESSION_CONNECTED(Session) ((Session)->ClientPipeHandle != NULL)
  92. /////////////////////////////////////////////////////////////////////////////
  93. //
  94. // CreateSession
  95. //
  96. // Creates a new session. Involves creating the shell process and establishing
  97. // pipes for communication with it.
  98. //
  99. // Returns a handle to the session or NULL on failure.
  100. //
  101. /////////////////////////////////////////////////////////////////////////////
  102. HANDLE
  103. CreateSession(
  104. HANDLE TokenToUse,
  105. PCOMMAND_HEADER LpCommandHeader
  106. )
  107. {
  108. PSESSION_DATA Session = NULL;
  109. BOOL Result;
  110. SECURITY_ATTRIBUTES SecurityAttributes;
  111. SECURITY_DESCRIPTOR SecurityDescriptor;
  112. HANDLE ShellStdinPipe = NULL;
  113. HANDLE ShellStdoutPipe = NULL;
  114. //
  115. // Allocate space for the session data
  116. //
  117. Session = (PSESSION_DATA)Alloc(sizeof(SESSION_DATA));
  118. if (Session == NULL) {
  119. return(NULL);
  120. }
  121. //
  122. // Reset fields in preparation for failure
  123. //
  124. Session->ShellReadPipeHandle = NULL;
  125. Session->ShellWritePipeHandle = NULL;
  126. Session->ShellReadAsyncHandle = NULL;
  127. Session->ShellWriteAsyncHandle = NULL;
  128. //
  129. // Create the I/O pipes for the shell - give world access so that spawned
  130. // command process can access them in client's contex
  131. //
  132. Result = InitializeSecurityDescriptor (
  133. &SecurityDescriptor,
  134. SECURITY_DESCRIPTOR_REVISION);
  135. if (!Result) {
  136. RcDbgPrint("Failed to initialize shell pipe security descriptor, error = %d\n",
  137. GetLastError());
  138. goto Failure;
  139. }
  140. Result = SetSecurityDescriptorDacl (
  141. &SecurityDescriptor,
  142. TRUE,
  143. NULL,
  144. FALSE);
  145. if (!Result) {
  146. RcDbgPrint("Failed to set shell pipe DACL, error = %d\n", GetLastError());
  147. goto Failure;
  148. }
  149. SecurityAttributes.nLength = sizeof(SecurityAttributes);
  150. SecurityAttributes.lpSecurityDescriptor = &SecurityDescriptor; // Use world-ACL
  151. SecurityAttributes.bInheritHandle = TRUE; // Shell will inherit handles
  152. Result = RcCreatePipe(&Session->ShellReadPipeHandle,
  153. &ShellStdoutPipe,
  154. &SecurityAttributes,
  155. 0, // Default pipe size
  156. 0, // Default timeout
  157. FILE_FLAG_OVERLAPPED, // shell read flags
  158. 0 // shell stdout flags
  159. );
  160. if (!Result) {
  161. RcDbgPrint("Failed to create shell stdout pipe, error = %d\n",
  162. GetLastError());
  163. goto Failure;
  164. }
  165. Result = RcCreatePipe(&ShellStdinPipe,
  166. &Session->ShellWritePipeHandle,
  167. &SecurityAttributes,
  168. 0, // Default pipe size
  169. 0, // Default timeout
  170. 0, // shell stdin flags
  171. FILE_FLAG_OVERLAPPED // shell write flags
  172. );
  173. if (!Result) {
  174. RcDbgPrint("Failed to create shell stdin pipe, error = %d\n",
  175. GetLastError());
  176. goto Failure;
  177. }
  178. //
  179. // Initialize async objects
  180. //
  181. Session->ShellReadAsyncHandle = CreateAsync(FALSE);
  182. if (Session->ShellReadAsyncHandle == NULL) {
  183. RcDbgPrint("Failed to create shell read async object, error = %d\n",
  184. GetLastError());
  185. goto Failure;
  186. }
  187. Session->ShellWriteAsyncHandle = CreateAsync(FALSE);
  188. if (Session->ShellWriteAsyncHandle == NULL) {
  189. RcDbgPrint("Failed to create shell write async object, error = %d\n",
  190. GetLastError());
  191. goto Failure;
  192. }
  193. Session->DefaultDirectory = GetDefaultDirectory();
  194. //
  195. // Start command shell with pipes connection to stdin/out/err
  196. //
  197. Session->ShellProcessHandle = StartShell(ShellStdinPipe,
  198. ShellStdoutPipe,
  199. Session, TokenToUse,
  200. LpCommandHeader);
  201. //
  202. // Close local handles
  203. //
  204. if (!(CloseHandle(ShellStdinPipe) &&
  205. CloseHandle(ShellStdoutPipe)))
  206. {
  207. ShellStdinPipe = NULL;
  208. ShellStdoutPipe = NULL;
  209. RcDbgPrint("Failed to close local pipe handles, error = %d\n",
  210. GetLastError());
  211. goto Failure;
  212. }
  213. //
  214. // Check result of shell start
  215. //
  216. if (Session->ShellProcessHandle == NULL)
  217. {
  218. RcDbgPrint("Failed to execute shell\n");
  219. goto Failure;
  220. }
  221. //
  222. // The session is not yet connected, initialize variables to indicate that
  223. //
  224. Session->ClientPipeHandle = NULL;
  225. //
  226. // Success, return the session pointer as a handle
  227. //
  228. return((HANDLE)Session);
  229. Failure:
  230. //
  231. // We get here for any failure case.
  232. // Free up any resources and exit
  233. //
  234. //
  235. // Cleanup shell pipe handles
  236. //
  237. if (ShellStdinPipe != NULL) {
  238. RcCloseHandle(ShellStdinPipe, "shell stdin pipe (shell side)");
  239. }
  240. if (ShellStdoutPipe != NULL) {
  241. RcCloseHandle(ShellStdoutPipe, "shell stdout pipe (shell side)");
  242. }
  243. if (Session->ShellReadPipeHandle != NULL) {
  244. RcCloseHandle(Session->ShellReadPipeHandle, "shell read pipe (session side)");
  245. }
  246. if (Session->ShellWritePipeHandle != NULL) {
  247. RcCloseHandle(Session->ShellWritePipeHandle, "shell write pipe (session side)");
  248. }
  249. //
  250. // Cleanup async data
  251. //
  252. if (Session->ShellReadAsyncHandle != NULL) {
  253. DeleteAsync(Session->ShellReadAsyncHandle);
  254. }
  255. if (Session->ShellWriteAsyncHandle != NULL) {
  256. DeleteAsync(Session->ShellWriteAsyncHandle);
  257. }
  258. //
  259. // Free up our session data
  260. //
  261. Free(Session);
  262. return(NULL);
  263. }
  264. CHAR *
  265. GetDefaultDirectory(
  266. void
  267. )
  268. {
  269. CHAR *HomeDirectory = (CHAR *)malloc(MAX_PATH * sizeof(CHAR));
  270. WIN32_FIND_DATA FileFindData;
  271. // Local system doesn't have many env. vars.
  272. // One that it does have is USERPROFILE. We'll try that first
  273. if (!GetEnvironmentVariable("USERPROFILE", HomeDirectory, MAX_PATH))
  274. {
  275. if (!GetEnvironmentVariable("TEMP", HomeDirectory, MAX_PATH))
  276. {
  277. if (!GetEnvironmentVariable("TMP", HomeDirectory, MAX_PATH))
  278. {
  279. RcDbgPrint("Can't find USERPROFILE, TEMP, TMP: defaulting to NULL");
  280. free(HomeDirectory);
  281. return NULL;
  282. }
  283. }
  284. }
  285. if (strlen((const char *)HomeDirectory) < MAX_DIRECTORY_LENGTH)
  286. {
  287. RcDbgPrint("Trying to use home directory %s\n", HomeDirectory);
  288. // Now verify that indeed the USERPROFILE directory is actually a directory
  289. // If not, then go for NULL
  290. if (INVALID_HANDLE_VALUE == FindFirstFile(HomeDirectory, &FileFindData))
  291. {
  292. free(HomeDirectory);
  293. return NULL;
  294. }
  295. else
  296. {
  297. if (!(FileFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  298. {
  299. free(HomeDirectory);
  300. return NULL;
  301. }
  302. }
  303. RcDbgPrint("HomeDirectory = %s\n", HomeDirectory);
  304. return HomeDirectory;
  305. }
  306. else
  307. {
  308. RcDbgPrint("Using NULL default directory\n");
  309. free(HomeDirectory);
  310. return NULL;
  311. }
  312. }
  313. /////////////////////////////////////////////////////////////////////////////
  314. //
  315. // DeleteSession
  316. //
  317. // Deletes the session specified by SessionHandle.
  318. //
  319. // Returns nothing
  320. //
  321. /////////////////////////////////////////////////////////////////////////////
  322. VOID
  323. DeleteSession(
  324. HANDLE SessionHandle
  325. )
  326. {
  327. PSESSION_DATA Session = (PSESSION_DATA)SessionHandle;
  328. BOOL Result;
  329. //
  330. // Kill off the shell process
  331. //
  332. Result = TerminateProcess(Session->ShellProcessHandle, 1);
  333. if (!Result) {
  334. RcDbgPrint("May have failed to terminate shell, error = %d\n", GetLastError());
  335. }
  336. RcCloseHandle(Session->ShellProcessHandle, "shell process");
  337. //
  338. // Close the shell pipe handles
  339. //
  340. RcCloseHandle(Session->ShellReadPipeHandle, "shell read pipe (session side)");
  341. RcCloseHandle(Session->ShellWritePipeHandle, "shell write pipe (session side)");
  342. //
  343. // Cleanup async data
  344. //
  345. DeleteAsync(Session->ShellReadAsyncHandle);
  346. DeleteAsync(Session->ShellWriteAsyncHandle);
  347. //
  348. // Free up the session structure
  349. //
  350. Free(Session);
  351. //
  352. // We're done
  353. //
  354. return;
  355. }
  356. /////////////////////////////////////////////////////////////////////////////
  357. //
  358. // ConnectSession
  359. //
  360. // Connects the session specified by SessionHandle to a client
  361. // on the other end of the pipe specified by PipeHandle
  362. //
  363. // Returns a session disconnect notification handle or NULL on failure.
  364. // The returned handle will be signalled if the client disconnects or the
  365. // shell terminates.
  366. //
  367. /////////////////////////////////////////////////////////////////////////////
  368. HANDLE
  369. ConnectSession(
  370. HANDLE SessionHandle,
  371. HANDLE ClientPipeHandle
  372. )
  373. {
  374. PSESSION_DATA Session = (PSESSION_DATA)SessionHandle;
  375. DWORD ThreadId;
  376. assert(ClientPipeHandle != NULL);
  377. //
  378. // Fail if the session is already connected
  379. //
  380. if (SESSION_CONNECTED(Session)) {
  381. RcDbgPrint("Attempted to connect session already connected\n");
  382. return(NULL);
  383. }
  384. //
  385. // Store the client pipe handle in the session structure so the thread
  386. // can get at it. This also signals that the session is connected.
  387. //
  388. Session->ClientPipeHandle = ClientPipeHandle;
  389. //
  390. // Create the session thread
  391. //
  392. Session->SessionThreadHandle = CreateThread(
  393. NULL,
  394. 0, // Default stack size
  395. (LPTHREAD_START_ROUTINE)SessionThreadFn, // Start address
  396. (LPVOID)Session, // Parameter
  397. 0, // Creation flags
  398. &ThreadId // Thread id
  399. );
  400. if (Session->SessionThreadHandle == NULL) {
  401. RcDbgPrint("Failed to create session thread, error = %d\n", GetLastError());
  402. //
  403. // Reset the client pipe handle to indicate this session is disconnected
  404. //
  405. Session->ClientPipeHandle = NULL;
  406. }
  407. return(Session->SessionThreadHandle);
  408. }
  409. /////////////////////////////////////////////////////////////////////////////
  410. //
  411. // StartShell
  412. //
  413. // Execs the shell with the specified handle as stdin, stdout/err
  414. //
  415. // Returns process handle or NULL on failure
  416. //
  417. /////////////////////////////////////////////////////////////////////////////
  418. HANDLE
  419. StartShell(
  420. HANDLE StdinPipeHandle,
  421. HANDLE StdoutPipeHandle,
  422. PSESSION_DATA Session,
  423. HANDLE TokenToUse,
  424. PCOMMAND_HEADER LpCommandHeader
  425. )
  426. {
  427. PROCESS_INFORMATION ProcessInformation;
  428. SECURITY_DESCRIPTOR SecurityDescriptor;
  429. STARTUPINFO si;
  430. HANDLE ProcessHandle = NULL;
  431. PROCESS_ACCESS_TOKEN ProcessTokenInfo;
  432. DWORD Result;
  433. UCHAR ShellCommandLine[MAX_CMD_LENGTH+SHELL_CMD_PREFIX_LEN+1];
  434. //
  435. // Detached process has no console - create new process with stdin,
  436. // stdout set to the passed-in handles
  437. //
  438. si.cb = sizeof(STARTUPINFO);
  439. si.lpReserved = NULL;
  440. si.lpTitle = SHELL_REMOTE_CMD_TITLE;
  441. si.lpDesktop = NULL;
  442. si.dwX = si.dwY = si.dwXSize = si.dwYSize = 0L;
  443. si.dwFlags = STARTF_USESTDHANDLES;
  444. si.hStdInput = StdinPipeHandle;
  445. si.hStdOutput = StdoutPipeHandle;
  446. si.hStdError = StdoutPipeHandle;
  447. si.wShowWindow = SW_SHOW;
  448. si.lpReserved2 = NULL;
  449. si.cbReserved2 = 0;
  450. //
  451. // Set command string for single command or interactive mode
  452. //
  453. if (LpCommandHeader->CommandFixedHeader.CommandLength == 0)
  454. {
  455. strcpy ((char *)ShellCommandLine, SHELL_COMMAND_LINE);
  456. }
  457. else
  458. {
  459. //
  460. // Construct command string for execute and exit
  461. //
  462. strcpy ((char *)ShellCommandLine, SHELL_COMMAND_PREFIX);
  463. strncat ((char *)ShellCommandLine,
  464. (const char *)LpCommandHeader->Command,
  465. LpCommandHeader->CommandFixedHeader.CommandLength);
  466. }
  467. //
  468. // Create process with stdin/out/err connected to session pipes.
  469. // Create suspended then set the process token to the token of the
  470. // connected client (primary version of impersonate token).
  471. //
  472. // What if I use CreateProcessAsUser here?
  473. if (CreateProcess(NULL,
  474. (char *)ShellCommandLine,
  475. NULL,
  476. NULL,
  477. TRUE, // Inherit handles
  478. CREATE_NEW_PROCESS_GROUP | CREATE_SUSPENDED , // | CREATE_NEW_CONSOLE
  479. NULL,
  480. Session->DefaultDirectory,
  481. &si,
  482. &ProcessInformation))
  483. {
  484. ProcessHandle = ProcessInformation.hProcess;
  485. Session->ShellProcessGroupId = ProcessInformation.dwProcessId;
  486. }
  487. else
  488. {
  489. RcDbgPrint("Failed to execute shell, error = %d\n", GetLastError());
  490. return(NULL);
  491. }
  492. //
  493. // Set process token for client and local system context access
  494. // BUGBUG - actually, world, for now
  495. //
  496. Result = InitializeSecurityDescriptor (
  497. &SecurityDescriptor,
  498. SECURITY_DESCRIPTOR_REVISION);
  499. if (!Result)
  500. {
  501. RcDbgPrint(
  502. "Failed to initialize shell process security descriptor, error = %d\n",
  503. GetLastError()
  504. );
  505. RcCloseHandle(ProcessInformation.hThread, "process thread");
  506. return(NULL);
  507. }
  508. Result = SetSecurityDescriptorDacl (
  509. &SecurityDescriptor,
  510. TRUE,
  511. NULL,
  512. FALSE);
  513. if (0 == Result) {
  514. RcDbgPrint("Failed to initialize shell process DACL, error = %d\n", GetLastError());
  515. RcCloseHandle(ProcessInformation.hThread, "process thread");
  516. return(NULL);
  517. }
  518. Result = SetKernelObjectSecurity(
  519. ProcessHandle,
  520. DACL_SECURITY_INFORMATION,
  521. &SecurityDescriptor );
  522. if (0 == Result) {
  523. RcDbgPrint("Failed to set DACL on client token, error = %d\n", GetLastError());
  524. RcCloseHandle(ProcessInformation.hThread, "process thread");
  525. return(NULL);
  526. }
  527. //
  528. // Now set the process token and resume execution
  529. //
  530. ProcessTokenInfo.Token = TokenToUse;
  531. ProcessTokenInfo.Thread = ProcessInformation.hThread;
  532. if (!NT_SUCCESS( NtSetInformationProcess(
  533. ProcessInformation.hProcess,
  534. ProcessAccessToken,
  535. &ProcessTokenInfo,
  536. sizeof(ProcessTokenInfo)))) {
  537. RcDbgPrint("Failed to set token");
  538. RcCloseHandle(ProcessInformation.hThread, "process thread");
  539. return (NULL);
  540. }
  541. Result = ResumeThread (ProcessInformation.hThread);
  542. if (Result != 1) {
  543. RcDbgPrint("Failed to resume shell, suspend = %d, error = %d\n", Result, GetLastError());
  544. RcCloseHandle(ProcessInformation.hThread, "process thread");
  545. return(NULL);
  546. }
  547. RcCloseHandle(ProcessInformation.hThread, "process thread");
  548. return(ProcessHandle);
  549. }
  550. /////////////////////////////////////////////////////////////////////////////
  551. //
  552. // SessionThreadFn
  553. //
  554. // This is the code executed by the session thread
  555. //
  556. // Waits for read or write from/to shell or client pipe and termination
  557. // event. Handles reads or writes by passing data to either client or
  558. // shell as appropriate. Any error or termination event being signalled
  559. // causes the thread to exit with an appropriate exit code.
  560. //
  561. /////////////////////////////////////////////////////////////////////////////
  562. DWORD
  563. SessionThreadFn(
  564. LPVOID Parameter
  565. )
  566. {
  567. PSESSION_DATA Session = (PSESSION_DATA)Parameter;
  568. HANDLE ClientReadAsyncHandle;
  569. HANDLE ClientWriteAsyncHandle;
  570. DWORD BytesTransferred;
  571. DWORD CompletionCode;
  572. BOOL Result;
  573. DWORD WaitResult;
  574. DWORD ExitCode;
  575. HANDLE WaitHandles[NUM_WAIT_HANDLES];
  576. BOOL Done;
  577. DWORD i;
  578. //
  579. // Initialize the client async structures
  580. //
  581. ClientReadAsyncHandle = CreateAsync(TRUE);
  582. if (ClientReadAsyncHandle == NULL) {
  583. RcDbgPrint("Failed to create client read async object, error = %d\n", GetLastError());
  584. return((DWORD)ConnectError);
  585. }
  586. ClientWriteAsyncHandle = CreateAsync(TRUE);
  587. if (ClientWriteAsyncHandle == NULL) {
  588. RcDbgPrint("Failed to create client write async object, error = %d\n", GetLastError());
  589. DeleteAsync(ClientReadAsyncHandle);
  590. return((DWORD)ConnectError);
  591. }
  592. //
  593. // Initialize the handle array we'll wait on
  594. //
  595. WaitHandles[0] = RcmdStopEvent;
  596. WaitHandles[1] = GetAsyncCompletionHandle(Session->ShellReadAsyncHandle);
  597. WaitHandles[2] = GetAsyncCompletionHandle(Session->ShellWriteAsyncHandle);
  598. WaitHandles[3] = GetAsyncCompletionHandle(ClientReadAsyncHandle);
  599. WaitHandles[4] = GetAsyncCompletionHandle(ClientWriteAsyncHandle);
  600. //
  601. // Wait on our handle array in a loop until an error occurs or
  602. // we're signalled to exit.
  603. //
  604. Done = FALSE;
  605. while (!Done) {
  606. //
  607. // Wait for one of our objects to be signalled.
  608. //
  609. WaitResult = WaitForMultipleObjects(NUM_WAIT_HANDLES, WaitHandles, FALSE, INFINITE);
  610. if (WaitResult == WAIT_FAILED) {
  611. RcDbgPrint("Session thread wait failed, error = %d\n", GetLastError());
  612. ExitCode = (DWORD)ConnectError;
  613. break; // out of while
  614. }
  615. switch (WaitResult-WAIT_OBJECT_0) {
  616. case 0:
  617. //
  618. // Service being stopped
  619. //
  620. RcDbgPrint("Service Shutdown\n");
  621. ExitCode = (DWORD)ServiceStopped;
  622. Done = TRUE;
  623. break; // out of switch
  624. case 1:
  625. //
  626. // Shell read completed
  627. //
  628. CompletionCode = GetAsyncResult(Session->ShellReadAsyncHandle,
  629. &BytesTransferred);
  630. if (CompletionCode != ERROR_SUCCESS) {
  631. RcDbgPrint("Async read from shell returned error, completion code = %d\n", CompletionCode);
  632. ExitCode = (DWORD)ShellEnded;
  633. Done = TRUE;
  634. break; // out of switch
  635. }
  636. //
  637. // Start an async write to client pipe
  638. //
  639. Result = WriteFileAsync(Session->ClientPipeHandle,
  640. Session->ShellReadBuffer,
  641. BytesTransferred,
  642. ClientWriteAsyncHandle);
  643. if (!Result) {
  644. RcDbgPrint("Async write to client pipe failed, error = %d\n", GetLastError());
  645. ExitCode = (DWORD)ClientDisconnected;
  646. Done = TRUE;
  647. }
  648. break; // out of switch
  649. case 4:
  650. //
  651. // Client write completed
  652. //
  653. CompletionCode = GetAsyncResult(ClientWriteAsyncHandle,
  654. &BytesTransferred);
  655. if (CompletionCode != ERROR_SUCCESS) {
  656. RcDbgPrint("Async write to client returned error, completion code = %d\n", CompletionCode);
  657. ExitCode = (DWORD)ClientDisconnected;
  658. Done = TRUE;
  659. break; // out of switch
  660. }
  661. //
  662. // Start an async read from shell
  663. //
  664. Result = ReadFileAsync(Session->ShellReadPipeHandle,
  665. Session->ShellReadBuffer,
  666. sizeof(Session->ShellReadBuffer),
  667. Session->ShellReadAsyncHandle);
  668. if (!Result) {
  669. RcDbgPrint("Async read from shell failed, error = %d\n", GetLastError());
  670. ExitCode = (DWORD)ShellEnded;
  671. Done = TRUE;
  672. }
  673. break; // out of switch
  674. case 3:
  675. //
  676. // Client read completed
  677. //
  678. CompletionCode = GetAsyncResult(ClientReadAsyncHandle,
  679. &BytesTransferred);
  680. if (CompletionCode != ERROR_SUCCESS) {
  681. RcDbgPrint("Async read from client returned error, completion code = %d\n", CompletionCode);
  682. ExitCode = (DWORD)ClientDisconnected;
  683. Done = TRUE;
  684. break; // out of switch
  685. }
  686. //
  687. // Check for Ctrl-C from the client
  688. //
  689. for (i=0; i < BytesTransferred; i++) {
  690. if (Session->ShellWriteBuffer[i] == '\003') {
  691. //
  692. // Generate control-C: use Ctrl-Break because ctrl-c is
  693. // disabled for process group
  694. //
  695. if (!(GenerateConsoleCtrlEvent (
  696. CTRL_BREAK_EVENT,
  697. Session->ShellProcessGroupId))) {
  698. RcDbgPrint("Ctrl-break event failure, error = %d\n", GetLastError());
  699. }
  700. //
  701. // Remove the Ctrl-C from the buffer
  702. //
  703. BytesTransferred --;
  704. for (; i < BytesTransferred; i++) {
  705. Session->ShellWriteBuffer[i] = Session->ShellWriteBuffer[i+1];
  706. }
  707. }
  708. }
  709. //
  710. // Start an async write to shell
  711. //
  712. Result = WriteFileAsync(Session->ShellWritePipeHandle,
  713. Session->ShellWriteBuffer,
  714. BytesTransferred,
  715. Session->ShellWriteAsyncHandle);
  716. if (!Result) {
  717. RcDbgPrint("Async write to shell failed, error = %d\n", GetLastError());
  718. ExitCode = (DWORD)ShellEnded;
  719. Done = TRUE;
  720. }
  721. break; // out of switch
  722. case 2:
  723. //
  724. // Shell write completed
  725. //
  726. CompletionCode = GetAsyncResult(Session->ShellWriteAsyncHandle,
  727. &BytesTransferred);
  728. if (CompletionCode != ERROR_SUCCESS) {
  729. RcDbgPrint("Async write to shell returned error, completion code = %d\n", CompletionCode);
  730. ExitCode = (DWORD)ShellEnded;
  731. Done = TRUE;
  732. break; // out of switch
  733. }
  734. //
  735. // Start an async read from client
  736. //
  737. Result = ReadFileAsync(Session->ClientPipeHandle,
  738. Session->ShellWriteBuffer,
  739. sizeof(Session->ShellWriteBuffer),
  740. ClientReadAsyncHandle);
  741. if (!Result) {
  742. RcDbgPrint("Async read from client failed, error = %d\n", GetLastError());
  743. ExitCode = (DWORD)ClientDisconnected;
  744. Done = TRUE;
  745. }
  746. break; // out of switch
  747. default:
  748. RcDbgPrint("Session thread, unexpected result from wait, result = %d\n", WaitResult);
  749. ExitCode = (DWORD)ConnectError;
  750. Done = TRUE;
  751. break;
  752. } // switch
  753. } // while(!done)
  754. //
  755. // Cleanup and exit
  756. //
  757. //
  758. // Closing the client pipe should interrupt any pending I/O so
  759. // we should then be safe to close the event handles in the client
  760. // overlapped structs
  761. //
  762. Result = DisconnectNamedPipe(Session->ClientPipeHandle);
  763. if (!Result) {
  764. RcDbgPrint("Session thread: disconnect client named pipe failed, error = %d\n", GetLastError());
  765. }
  766. RcCloseHandle(Session->ClientPipeHandle, "client pipe");
  767. Session->ClientPipeHandle = NULL;
  768. //
  769. // Delete client async objects
  770. //
  771. DeleteAsync(ClientReadAsyncHandle);
  772. DeleteAsync(ClientWriteAsyncHandle);
  773. //
  774. // Terminate shell process, close shell pipe handles, and free session
  775. // structure
  776. //
  777. DeleteSession (Session);
  778. //
  779. // Return the appropriate exit code
  780. //
  781. ExitThread(ExitCode);
  782. assert(FALSE);
  783. return(ExitCode); // keep compiler happy
  784. }
  785.