SecureShell.cpp 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include <stdio.h>
  5. #include "PuttyIntf.h"
  6. #include "Net.h"
  7. #include "Interface.h"
  8. #include "SecureShell.h"
  9. #include "TextsCore.h"
  10. #include "HelpCore.h"
  11. #include "Common.h"
  12. #include "ScpMain.h"
  13. #include "Security.h"
  14. #ifndef AUTO_WINSOCK
  15. #include <winsock2.h>
  16. #endif
  17. //---------------------------------------------------------------------------
  18. #pragma package(smart_init)
  19. //---------------------------------------------------------------------------
  20. #define MAX_BUFSIZE 32768
  21. const TColor LogLineColors[] =
  22. {clGreen, clRed, clMaroon, clBlue, clGray};
  23. //---------------------------------------------------------------------------
  24. __fastcall TSecureShell::TSecureShell()
  25. {
  26. FSessionData = new TSessionData("");
  27. FActive = False;
  28. PendLen = 0;
  29. PendSize = 0;
  30. FStdErrorTemp = "";
  31. FBytesSent = 0;
  32. FBytesReceived = 0;
  33. FLog = new TSessionLog(this);
  34. FOnQueryUser = NULL;
  35. FOnPromptUser = NULL;
  36. FOnShowExtendedException = NULL;
  37. FOnUpdateStatus = NULL;
  38. FOnStdError = NULL;
  39. FOnClose = NULL;
  40. FCSCipher = cipWarn; // = not detected yet
  41. FSCCipher = cipWarn;
  42. FReachedStatus = 0;
  43. UpdateStatus(sshClosed);
  44. FConfig = new Config();
  45. FSocket = new SOCKET;
  46. FMaxPacketSize = NULL;
  47. FBufSize = 0;
  48. }
  49. //---------------------------------------------------------------------------
  50. __fastcall TSecureShell::~TSecureShell()
  51. {
  52. ClearStdError();
  53. Active = false;
  54. SAFE_DESTROY(FSessionData);
  55. SAFE_DESTROY(FLog);
  56. delete FConfig;
  57. FConfig = NULL;
  58. UserObject = NULL;
  59. delete FSocket;
  60. FSocket = NULL;
  61. }
  62. //---------------------------------------------------------------------------
  63. void __fastcall TSecureShell::Open()
  64. {
  65. const char * InitError;
  66. char * RealHost;
  67. FStoredPasswordTried = false;
  68. FStoredPasswordTriedForKI = false;
  69. FReachedStatus = 0;
  70. {
  71. TCoreGuard Guard;
  72. SessionsCount++;
  73. if (SessionsCount == 1)
  74. {
  75. UpdateStatus(sshInitWinSock);
  76. NetInitialize();
  77. }
  78. else
  79. {
  80. Log->Id = reinterpret_cast<unsigned int>(this);
  81. }
  82. }
  83. Log->AddStartupInfo();
  84. Active = false;
  85. FBackend = &ssh_backend;
  86. FAuthenticationLog = "";
  87. UpdateStatus(sshLookupHost);
  88. SessionData->StoreToConfig(FConfig);
  89. InitError = FBackend->init(this, &FBackendHandle, FConfig,
  90. SessionData->HostName.c_str(), SessionData->PortNumber, &RealHost, 0,
  91. FConfig->tcp_keepalives);
  92. if (InitError)
  93. {
  94. FatalError(InitError);
  95. }
  96. FRealHost = RealHost;
  97. UpdateStatus(sshConnect);
  98. /*FLoggingContext = log_init(this, (void *)FConfig);
  99. FBackend->provide_logctx(FBackendHandle, FLoggingContext);*/
  100. Init();
  101. CheckConnection(CONNECTION_FAILED);
  102. FLastDataSent = Now();
  103. FLoginTime = Now();
  104. Log->AddSeparator();
  105. UpdateStatus(sshAuthenticated);
  106. }
  107. //---------------------------------------------------------------------------
  108. void __fastcall TSecureShell::Init()
  109. {
  110. try
  111. {
  112. try
  113. {
  114. while (!FBackend->sendok(FBackendHandle))
  115. {
  116. if (Configuration->LogProtocol >= 1)
  117. {
  118. LogEvent("Waiting for the server to continue with the initialisation");
  119. }
  120. WaitForData(true);
  121. }
  122. }
  123. catch(Exception & E)
  124. {
  125. if ((FReachedStatus == sshAuthenticate) && !FAuthenticationLog.IsEmpty())
  126. {
  127. FatalError(&E, FMTLOAD(AUTHENTICATION_LOG, (FAuthenticationLog)));
  128. }
  129. else
  130. {
  131. throw;
  132. }
  133. }
  134. }
  135. catch(Exception & E)
  136. {
  137. if (FReachedStatus == sshAuthenticate)
  138. {
  139. FatalError(&E, LoadStr(AUTHENTICATION_FAILED));
  140. }
  141. else
  142. {
  143. throw;
  144. }
  145. }
  146. }
  147. //---------------------------------------------------------------------------
  148. void __fastcall TSecureShell::PuttyLogEvent(const AnsiString & Str)
  149. {
  150. #define SERVER_VERSION_MSG "Server version: "
  151. // Gross hack
  152. if (Str.Pos(SERVER_VERSION_MSG) == 1)
  153. {
  154. FSshVersionString = Str.SubString(strlen(SERVER_VERSION_MSG) + 1,
  155. Str.Length() - strlen(SERVER_VERSION_MSG));
  156. }
  157. LogEvent(Str);
  158. }
  159. //---------------------------------------------------------------------------
  160. AnsiString __fastcall TSecureShell::GetSshImplementation()
  161. {
  162. const char * Ptr = strchr(FSshVersionString.c_str(), '-');
  163. if (Ptr != NULL)
  164. {
  165. Ptr = strchr(Ptr + 1, '-');
  166. }
  167. return (Ptr != NULL) ? AnsiString(Ptr + 1) : AnsiString();
  168. }
  169. //---------------------------------------------------------------------
  170. AnsiString __fastcall TSecureShell::GetPassword()
  171. {
  172. return (FPassword.IsEmpty() ? AnsiString() :
  173. DecryptPassword(FPassword, SessionData->SessionName));
  174. }
  175. //---------------------------------------------------------------------
  176. bool __fastcall TSecureShell::GetStoredPasswordTried()
  177. {
  178. return FStoredPasswordTried || FStoredPasswordTriedForKI;
  179. }
  180. //---------------------------------------------------------------------------
  181. TDateTime __fastcall TSecureShell::GetIdleInterval()
  182. {
  183. return (FSessionData->PingType != ptOff) ? FSessionData->PingIntervalDT :
  184. TDateTime(0);
  185. }
  186. //---------------------------------------------------------------------------
  187. bool __fastcall TSecureShell::PromptUser(const AnsiString Prompt,
  188. AnsiString & Response, bool IsPassword)
  189. {
  190. USEDPARAM(IsPassword);
  191. assert(IsPassword); // false only for username prompts
  192. bool Result;
  193. if (Prompt.Pos("Passphrase for key ") == 1)
  194. {
  195. AnsiString Key(Prompt);
  196. int P = Prompt.Pos("\"");
  197. if (P > 0)
  198. {
  199. Key.Delete(1, P);
  200. P = Key.LastDelimiter("\"");
  201. if (P > 0)
  202. {
  203. Key.SetLength(P - 1);
  204. }
  205. }
  206. LogEvent(FORMAT("Passphrase prompt (%s)", (Prompt)));
  207. Result = DoPromptUser(FMTLOAD(PROMPT_KEY_PASSPHRASE, (Key)),
  208. pkPassphrase, Response);
  209. }
  210. else if (Prompt.Pos("'s password: "))
  211. {
  212. LogEvent(FORMAT("Session password prompt (%s)", (Prompt)));
  213. if (!SessionData->Password.IsEmpty() && !FStoredPasswordTried)
  214. {
  215. LogEvent("Using stored password.");
  216. Result = true;
  217. Response = SessionData->Password;
  218. FStoredPasswordTried = true;
  219. }
  220. else
  221. {
  222. LogEvent("Asking user for password.");
  223. Result = DoPromptUser(
  224. FMTLOAD(PROMPT_SESSION_PASSWORD, (SessionData->SessionName)),
  225. pkPassword, Response);
  226. }
  227. }
  228. else
  229. {
  230. // in other cases we assume TIS/Cryptocard/keyboard-interactive authentification prompt
  231. LogEvent(FORMAT("%s prompt from server", (Prompt)));
  232. if (!SessionData->Password.IsEmpty() &&
  233. SessionData->AuthKIPassword && !FStoredPasswordTriedForKI)
  234. {
  235. LogEvent("Responding with stored password.");
  236. Result = true;
  237. Response = SessionData->Password;
  238. FStoredPasswordTriedForKI = true;
  239. }
  240. else
  241. {
  242. LogEvent("Asking user for response.");
  243. static const AnsiString ResponseSuffix("\r\nResponse: ");
  244. // Strip Cryptocard/TIS "Response" suffix
  245. AnsiString UserPrompt = Prompt;
  246. if (UserPrompt.SubString(UserPrompt.Length() - ResponseSuffix.Length() + 1,
  247. ResponseSuffix.Length()) == ResponseSuffix)
  248. {
  249. UserPrompt.SetLength(UserPrompt.Length() - ResponseSuffix.Length());
  250. }
  251. Result = DoPromptUser(UserPrompt, pkServerPrompt, Response);
  252. }
  253. };
  254. if (Configuration->RememberPassword)
  255. {
  256. FPassword = EncryptPassword(Response, SessionData->SessionName);
  257. }
  258. return Result;
  259. }
  260. //---------------------------------------------------------------------------
  261. bool __fastcall TSecureShell::DoPromptUser(AnsiString Prompt, TPromptKind Kind,
  262. AnsiString & Response)
  263. {
  264. bool Result = false;
  265. if (OnPromptUser != NULL)
  266. {
  267. OnPromptUser(this, Prompt, Kind, Response, Result, NULL);
  268. }
  269. return Result;
  270. }
  271. //---------------------------------------------------------------------------
  272. void __fastcall TSecureShell::GotHostKey()
  273. {
  274. // due to re-key GotHostKey() may be called again later during session
  275. if (FReachedStatus < sshAuthenticate)
  276. {
  277. UpdateStatus(sshAuthenticate);
  278. }
  279. }
  280. //---------------------------------------------------------------------------
  281. void __fastcall TSecureShell::FromBackend(Boolean IsStdErr, char * Data, Integer Length)
  282. {
  283. CheckConnection();
  284. // Following is taken from scp.c from_backend() and modified
  285. if (IsStdErr)
  286. {
  287. AddStdError(AnsiString(Data, Length));
  288. }
  289. else
  290. {
  291. unsigned char *p = (unsigned char *)Data;
  292. unsigned Len = (unsigned)Length;
  293. // If this is before the real session begins, raise exception.
  294. if (!OutPtr)
  295. {
  296. FatalError("Internal error: Session not yet begun.");
  297. }
  298. if ((OutLen > 0) && (Len > 0))
  299. {
  300. unsigned Used = OutLen;
  301. if (Used > Len) Used = Len;
  302. memcpy(OutPtr, p, Used);
  303. OutPtr += Used; OutLen -= Used;
  304. p += Used; Len -= Used;
  305. }
  306. if (Len > 0)
  307. {
  308. if (PendSize < PendLen + Len)
  309. {
  310. PendSize = PendLen + Len + 4096;
  311. Pending = (char *)
  312. (Pending ? srealloc(Pending, PendSize) : smalloc(PendSize));
  313. if (!Pending) FatalError("Out of memory");
  314. }
  315. memcpy(Pending + PendLen, p, Len);
  316. PendLen += Len;
  317. }
  318. }
  319. }
  320. //---------------------------------------------------------------------------
  321. Integer __fastcall TSecureShell::Receive(char * Buf, Integer Len)
  322. {
  323. CheckConnection();
  324. if (Len > 0)
  325. {
  326. // Following is taken from scp.c ssh_scp_recv() and modified
  327. OutPtr = Buf;
  328. OutLen = Len;
  329. /*
  330. * See if the pending-input block contains some of what we
  331. * need.
  332. */
  333. if (PendLen > 0)
  334. {
  335. unsigned PendUsed = PendLen;
  336. if (PendUsed > OutLen)
  337. {
  338. PendUsed = OutLen;
  339. }
  340. memcpy(OutPtr, Pending, PendUsed);
  341. memmove(Pending, Pending + PendUsed, PendLen - PendUsed);
  342. OutPtr += PendUsed;
  343. OutLen -= PendUsed;
  344. PendLen -= PendUsed;
  345. if (PendLen == 0)
  346. {
  347. PendSize = 0;
  348. sfree(Pending);
  349. Pending = NULL;
  350. }
  351. }
  352. // I don't undestand this yet, but it works :-)
  353. while (OutLen > 0)
  354. {
  355. if (Configuration->LogProtocol >= 1)
  356. {
  357. LogEvent(FORMAT("Waiting for another %u bytes", (static_cast<int>(OutLen))));
  358. }
  359. WaitForData(false);
  360. }
  361. // This seems ambiguous
  362. if (Len <= 0) FatalError(LoadStr(LOST_CONNECTION));
  363. };
  364. FBytesReceived += Len;
  365. return Len;
  366. }
  367. //---------------------------------------------------------------------------
  368. AnsiString __fastcall TSecureShell::ReceiveLine()
  369. {
  370. unsigned Index;
  371. Char Ch;
  372. AnsiString Line;
  373. Boolean EOL = False;
  374. do
  375. {
  376. // If there is any buffer of received chars
  377. if (PendLen > 0)
  378. {
  379. Index = 0;
  380. // Repeat until we walk thru whole buffer or reach end-of-line
  381. while ((Index < PendLen) && (!Index || (Pending[Index-1] != '\n')))
  382. {
  383. Index++;
  384. }
  385. EOL = (Boolean)(Index && (Pending[Index-1] == '\n'));
  386. Integer PrevLen = Line.Length();
  387. Line.SetLength(PrevLen + Index);
  388. Receive(Line.c_str() + PrevLen, Index);
  389. }
  390. // If buffer don't contain end-of-line character
  391. // we read one more which causes receiving new buffer of chars
  392. if (!EOL)
  393. {
  394. Receive(&Ch, 1);
  395. Line += Ch;
  396. EOL = (Ch == '\n');
  397. }
  398. }
  399. while (!EOL);
  400. // We don't want end-of-line character
  401. Line.SetLength(Line.Length()-1);
  402. Log->Add(llOutput, Line);
  403. return Line;
  404. }
  405. //---------------------------------------------------------------------------
  406. void __fastcall TSecureShell::SendSpecial(int Code)
  407. {
  408. LogEvent(FORMAT("Sending special code: %d", (Code)));
  409. CheckConnection();
  410. FBackend->special(FBackendHandle, (Telnet_Special)Code);
  411. FLastDataSent = Now();
  412. }
  413. //---------------------------------------------------------------------------
  414. void __fastcall TSecureShell::SendEOF()
  415. {
  416. SendSpecial(TS_EOF);
  417. }
  418. //---------------------------------------------------------------------------
  419. void __fastcall TSecureShell::Send(const char * Buf, Integer Len)
  420. {
  421. CheckConnection();
  422. FBufSize = FBackend->send(FBackendHandle, (char *)Buf, Len);
  423. if (Configuration->LogProtocol >= 1)
  424. {
  425. LogEvent(FORMAT("There are %u bytes remaining in the send buffer", (FBufSize)));
  426. }
  427. FLastDataSent = Now();
  428. FBytesSent += Len;
  429. while (FBufSize > MAX_BUFSIZE)
  430. {
  431. if (Configuration->LogProtocol >= 1)
  432. {
  433. LogEvent(FORMAT("There are %u bytes remaining in the send buffer, "
  434. "need to send at least another %u bytes",
  435. (FBufSize, FBufSize - MAX_BUFSIZE)));
  436. }
  437. // it seems that this does not work anyway
  438. // (i.e. once the send buffer fills we hang here)
  439. WaitForData(true);
  440. FBufSize = FBackend->sendbuffer(FBackendHandle);
  441. if (Configuration->LogProtocol >= 1)
  442. {
  443. LogEvent(FORMAT("There are %u bytes remaining in the send buffer", (FBufSize)));
  444. }
  445. }
  446. }
  447. //---------------------------------------------------------------------------
  448. void __fastcall TSecureShell::SendNull()
  449. {
  450. LogEvent("Sending NULL.");
  451. Send("", 1);
  452. }
  453. //---------------------------------------------------------------------------
  454. void __fastcall TSecureShell::SendStr(AnsiString Str)
  455. {
  456. CheckConnection();
  457. Send(Str.c_str(), Str.Length());
  458. }
  459. //---------------------------------------------------------------------------
  460. void __fastcall TSecureShell::SendLine(AnsiString Line)
  461. {
  462. SendStr(Line);
  463. Send("\n", 1);
  464. Log->Add(llInput, Line);
  465. }
  466. //---------------------------------------------------------------------------
  467. void __fastcall TSecureShell::AddStdError(AnsiString Str)
  468. {
  469. StdError += Str;
  470. Integer P;
  471. Str = DeleteChar(Str, '\r');
  472. // We send only whole line at once to log, so we have to cache
  473. // incoming std error data
  474. FStdErrorTemp += Str;
  475. AnsiString Line;
  476. // Do we have at least one complete line in std error cache?
  477. while ((P = FStdErrorTemp.Pos("\n")) > 0)
  478. {
  479. Line = FStdErrorTemp.SubString(1, P-1);
  480. FStdErrorTemp.Delete(1, P);
  481. AddStdErrorLine(Line);
  482. }
  483. }
  484. //---------------------------------------------------------------------------
  485. void __fastcall TSecureShell::AddStdErrorLine(const AnsiString Str)
  486. {
  487. if (OnStdError == NULL)
  488. {
  489. if (Status == sshAuthenticate)
  490. {
  491. FAuthenticationLog += (FAuthenticationLog.IsEmpty() ? "" : "\n") + Str;
  492. }
  493. }
  494. else
  495. {
  496. OnStdError(this, llStdError, Str);
  497. }
  498. Log->Add(llStdError, Str);
  499. }
  500. //---------------------------------------------------------------------------
  501. void __fastcall TSecureShell::ClearStdError()
  502. {
  503. // Flush std error cache
  504. if (!FStdErrorTemp.IsEmpty())
  505. {
  506. if (Status == sshAuthenticate)
  507. {
  508. FAuthenticationLog +=
  509. (FAuthenticationLog.IsEmpty() ? "" : "\n") + FStdErrorTemp;
  510. }
  511. Log->Add(llStdError, FStdErrorTemp);
  512. FStdErrorTemp = "";
  513. }
  514. StdError = "";
  515. }
  516. //---------------------------------------------------------------------------
  517. void __fastcall TSecureShell::FatalError(Exception * E, AnsiString Msg)
  518. {
  519. if (FActive)
  520. {
  521. // We log this instead of exception handler, because Close() would
  522. // probably cause exception handler to loose pointer to TShellLog()
  523. LogEvent("Attempt to close connection due to fatal exception:");
  524. Log->Add(llException, Msg);
  525. Log->AddException(E);
  526. Close();
  527. }
  528. SSH_FATAL_ERROR_EXT(E, Msg);
  529. }
  530. //---------------------------------------------------------------------------
  531. void __fastcall TSecureShell::FatalError(AnsiString Error)
  532. {
  533. FatalError(NULL, Error);
  534. }
  535. //---------------------------------------------------------------------------
  536. void __fastcall TSecureShell::SetSocket(void * value)
  537. {
  538. assert(value);
  539. // now this can be called repeatedly, so allow being called again with
  540. // the same socket
  541. if (FActive && (*static_cast<SOCKET*>(value) != INVALID_SOCKET) &&
  542. (*static_cast<SOCKET*>(FSocket) != *static_cast<SOCKET*>(value)))
  543. FatalError("Cannot set socket during connection");
  544. assert(FSocket);
  545. *static_cast<SOCKET*>(FSocket) = *static_cast<SOCKET*>(value);
  546. if (*static_cast<SOCKET*>(FSocket) != INVALID_SOCKET)
  547. {
  548. FActive = true;
  549. }
  550. else
  551. {
  552. Discard();
  553. }
  554. }
  555. //---------------------------------------------------------------------------
  556. void __fastcall TSecureShell::SetSessionData(TSessionData * value)
  557. {
  558. assert(!FActive);
  559. FSessionData->Assign(value);
  560. }
  561. //---------------------------------------------------------------------------
  562. void __fastcall TSecureShell::SetActive(bool value)
  563. {
  564. if (FActive != value)
  565. {
  566. if (value)
  567. {
  568. Open();
  569. }
  570. else
  571. {
  572. Close();
  573. }
  574. }
  575. }
  576. //---------------------------------------------------------------------------
  577. bool __fastcall TSecureShell::GetActive() const
  578. {
  579. return FActive;
  580. }
  581. //---------------------------------------------------------------------------
  582. void __fastcall TSecureShell::Discard()
  583. {
  584. bool WasActive = FActive;
  585. FActive = false;
  586. if (WasActive)
  587. {
  588. if (FReachedStatus)
  589. {
  590. TCoreGuard Guard;
  591. SessionsCount--;
  592. if (SessionsCount == 0)
  593. {
  594. NetFinalize();
  595. }
  596. }
  597. if (OnClose)
  598. {
  599. OnClose(this);
  600. }
  601. }
  602. FStatus = sshClosed;
  603. }
  604. //---------------------------------------------------------------------------
  605. void __fastcall TSecureShell::Close()
  606. {
  607. LogEvent("Closing connection.");
  608. CheckConnection();
  609. FBackend->free(FBackendHandle);
  610. Discard();
  611. }
  612. //---------------------------------------------------------------------------
  613. void inline __fastcall TSecureShell::CheckConnection(int Message)
  614. {
  615. if (!FActive || get_ssh_state_closed(FBackendHandle))
  616. {
  617. AnsiString Str = LoadStr(Message >= 0 ? Message : NOT_CONNECTED);
  618. int ExitCode = get_ssh_exitcode(FBackendHandle);
  619. if (ExitCode >= 0)
  620. {
  621. Str += " " + FMTLOAD(SSH_EXITCODE, (ExitCode));
  622. }
  623. FatalError(Str);
  624. }
  625. }
  626. //---------------------------------------------------------------------------
  627. bool __fastcall TSecureShell::Select(int Sec)
  628. {
  629. CheckConnection();
  630. struct timeval time;
  631. fd_set readfds;
  632. FD_ZERO(&readfds);
  633. FD_SET(*static_cast<SOCKET*>(FSocket), &readfds);
  634. time.tv_sec = Sec;
  635. time.tv_usec = 0;
  636. int R = select(1, &readfds, NULL, NULL, &time);
  637. if (R < 0)
  638. {
  639. SSH_FATAL_ERROR(FMTLOAD(UNKNOWN_SOCKET_STATUS, (R)));
  640. }
  641. if (Configuration->LogProtocol >= 2)
  642. {
  643. LogEvent(FORMAT("Select result is %d", (R)));
  644. }
  645. return (R > 0);
  646. }
  647. //---------------------------------------------------------------------------
  648. void __fastcall TSecureShell::PoolForData(unsigned int & Result)
  649. {
  650. if (Configuration->LogProtocol >= 2)
  651. {
  652. LogEvent("Pooling for data in case they finally arrives");
  653. }
  654. if (Select(0))
  655. {
  656. LogEvent("Data has arrived, closing query to user.");
  657. Result = qaRetry;
  658. }
  659. }
  660. //---------------------------------------------------------------------------
  661. extern int select_result(WPARAM, LPARAM);
  662. void __fastcall TSecureShell::WaitForData(bool Sending)
  663. {
  664. bool NeedToWait = true;
  665. SOCKET & Socket = *static_cast<SOCKET*>(FSocket);
  666. if (socket_writable(Socket))
  667. {
  668. if (Configuration->LogProtocol >= 1)
  669. {
  670. LogEvent("Checking low level send buffer");
  671. }
  672. select_result((WPARAM)(Socket), (LPARAM)FD_WRITE);
  673. }
  674. if (FBufSize > 0)
  675. {
  676. if (Configuration->LogProtocol >= 1)
  677. {
  678. LogEvent(FORMAT("Trying to dispatch send buffer (%u bytes)", (FBufSize)));
  679. }
  680. int NewBufSize = FBackend->send(FBackendHandle, "", 0);
  681. if ((NewBufSize < FBufSize) && Sending)
  682. {
  683. NeedToWait = false;
  684. }
  685. FBufSize = NewBufSize;
  686. if (Configuration->LogProtocol >= 1)
  687. {
  688. LogEvent(FORMAT("There are %u bytes remaining in the send buffer", (FBufSize)));
  689. }
  690. }
  691. bool IncomingData;
  692. if (!NeedToWait)
  693. {
  694. if (Configuration->LogProtocol >= 2)
  695. {
  696. LogEvent("Looking for incoming data");
  697. }
  698. // This is just attempt to make it as close as possible to previous behaviour
  699. // Maybe it is not necessary at all.
  700. IncomingData = Select(0);
  701. }
  702. else
  703. {
  704. do
  705. {
  706. if (Configuration->LogProtocol >= 2)
  707. {
  708. LogEvent("Looking for incoming data");
  709. }
  710. IncomingData = Select(FSessionData->Timeout);
  711. if (!IncomingData)
  712. {
  713. LogEvent("Waiting for data timed out, asking user what to do.");
  714. TQueryParams Params(qpFatalAbort | qpAllowContinueOnError);
  715. Params.Timer = 500;
  716. Params.TimerEvent = PoolForData;
  717. Params.TimerMessage = FMTLOAD(TIMEOUT_STILL_WAITING, (FSessionData->Timeout));
  718. Params.TimerAnswers = qaAbort;
  719. if (DoQueryUser(FMTLOAD(CONFIRM_PROLONG_TIMEOUT, (FSessionData->Timeout)),
  720. qaRetry | qaAbort, &Params) != qaRetry)
  721. {
  722. FatalError(LoadStr(USER_TERMINATED));
  723. }
  724. }
  725. }
  726. while (!IncomingData);
  727. }
  728. if (IncomingData)
  729. {
  730. select_result((WPARAM)(Socket), (LPARAM)FD_READ);
  731. }
  732. }
  733. //---------------------------------------------------------------------------
  734. bool __fastcall TSecureShell::SshFallbackCmd() const
  735. {
  736. return ssh_fallback_cmd(FBackendHandle);
  737. }
  738. //---------------------------------------------------------------------------
  739. void __fastcall TSecureShell::Error(const AnsiString Error) const
  740. {
  741. SSH_ERROR(Error);
  742. }
  743. //---------------------------------------------------------------------------
  744. extern int (WINAPI *p_WSAEnumNetworkEvents)
  745. (SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents);
  746. //---------------------------------------------------------------------------
  747. void __fastcall TSecureShell::Idle()
  748. {
  749. if (Configuration->LogProtocol >= 1)
  750. {
  751. LogEvent("Session upkeep");
  752. }
  753. noise_regular();
  754. // Keep session alive
  755. if ((FSessionData->PingType != ptOff) &&
  756. (Now() - FLastDataSent > FSessionData->PingIntervalDT))
  757. {
  758. KeepAlive();
  759. // in case keep alive could not be processed, postpone next attempt
  760. FLastDataSent = Now();
  761. }
  762. call_ssh_timer(FBackendHandle);
  763. // to let detect dropped connection immediatelly, also to let
  764. // process SSH-level communication with the server (KEX particularly)
  765. if (Select(0))
  766. {
  767. LogEvent("Detected incoming data while idle");
  768. select_result((WPARAM)(*static_cast<SOCKET*>(FSocket)), (LPARAM)FD_READ);
  769. CheckConnection();
  770. }
  771. }
  772. //---------------------------------------------------------------------------
  773. void __fastcall TSecureShell::KeepAlive()
  774. {
  775. LogEvent("Sending null packet to keep session alive.");
  776. SendSpecial(TS_PING);
  777. }
  778. //---------------------------------------------------------------------------
  779. void __fastcall TSecureShell::SetLog(TSessionLog * value)
  780. {
  781. FLog->Assign(value);
  782. }
  783. //---------------------------------------------------------------------------
  784. void __fastcall TSecureShell::SetConfiguration(TConfiguration *value)
  785. {
  786. FConfiguration = value;
  787. Log->Configuration = value;
  788. }
  789. //---------------------------------------------------------------------------
  790. TDateTime __fastcall TSecureShell::GetDuration() const
  791. {
  792. return Now() - FLoginTime;
  793. }
  794. //---------------------------------------------------------------------------
  795. int __fastcall TSecureShell::RemainingSendBuffer()
  796. {
  797. return MAX_BUFSIZE - FBufSize;
  798. }
  799. //---------------------------------------------------------------------------
  800. unsigned long __fastcall TSecureShell::MaxPacketSize()
  801. {
  802. if (SshVersion == 1)
  803. {
  804. return 0;
  805. }
  806. else
  807. {
  808. if (FMaxPacketSize == NULL)
  809. {
  810. FMaxPacketSize = ssh2_remmaxpkt(FBackendHandle);
  811. }
  812. return *FMaxPacketSize;
  813. }
  814. }
  815. //---------------------------------------------------------------------------
  816. TCompressionType __fastcall TSecureShell::FuncToCompression(
  817. const void * Compress) const
  818. {
  819. if (SshVersion == 1)
  820. {
  821. return get_ssh1_compressing(FBackendHandle) ? ctZLib : ctNone;
  822. }
  823. else
  824. {
  825. return (ssh_compress *)Compress == &ssh_zlib ? ctZLib : ctNone;
  826. }
  827. }
  828. //---------------------------------------------------------------------------
  829. TCompressionType __fastcall TSecureShell::GetCSCompression() const
  830. {
  831. assert(Active);
  832. return FuncToCompression(get_cscomp(FBackendHandle));
  833. }
  834. //---------------------------------------------------------------------------
  835. TCompressionType __fastcall TSecureShell::GetSCCompression() const
  836. {
  837. assert(Active);
  838. return FuncToCompression(get_sccomp(FBackendHandle));
  839. }
  840. //---------------------------------------------------------------------------
  841. int __fastcall TSecureShell::GetSshVersion() const
  842. {
  843. assert(Active);
  844. return get_ssh_version(FBackendHandle);
  845. }
  846. //---------------------------------------------------------------------------
  847. TCipher __fastcall TSecureShell::FuncToSsh1Cipher(const void * Cipher) const
  848. {
  849. const ssh_cipher *CipherFuncs[] =
  850. {&ssh_3des, &ssh_des, &ssh_blowfish_ssh1};
  851. const TCipher TCiphers[] = {cip3DES, cipDES, cipBlowfish};
  852. assert(LENOF(CipherFuncs) == LENOF(TCiphers));
  853. TCipher Result = cipWarn;
  854. for (int Index = 0; Index < LENOF(TCiphers); Index++)
  855. {
  856. if ((ssh_cipher *)Cipher == CipherFuncs[Index])
  857. {
  858. Result = TCiphers[Index];
  859. }
  860. }
  861. assert(Result != cipWarn);
  862. return Result;
  863. }
  864. //---------------------------------------------------------------------------
  865. TCipher __fastcall TSecureShell::FuncToSsh2Cipher(const void * Cipher) const
  866. {
  867. const ssh2_ciphers *CipherFuncs[] =
  868. {&ssh2_3des, &ssh2_des, &ssh2_aes, &ssh2_blowfish};
  869. const TCipher TCiphers[] = {cip3DES, cipDES, cipAES, cipBlowfish};
  870. assert(LENOF(CipherFuncs) == LENOF(TCiphers));
  871. TCipher Result = cipWarn;
  872. for (int C = 0; C < LENOF(TCiphers); C++)
  873. {
  874. for (int F = 0; F < CipherFuncs[C]->nciphers; F++)
  875. {
  876. if ((ssh2_cipher *)Cipher == CipherFuncs[C]->list[F])
  877. {
  878. Result = TCiphers[C];
  879. }
  880. }
  881. }
  882. assert(Result != cipWarn);
  883. return Result;
  884. }
  885. //---------------------------------------------------------------------------
  886. TCipher __fastcall TSecureShell::GetCSCipher()
  887. {
  888. assert(Active);
  889. if (FCSCipher == cipWarn)
  890. {
  891. if (SshVersion == 1)
  892. {
  893. FCSCipher = FuncToSsh1Cipher(get_cipher(FBackendHandle));
  894. }
  895. else
  896. {
  897. FCSCipher = FuncToSsh2Cipher(get_cscipher(FBackendHandle));
  898. }
  899. }
  900. return FCSCipher;
  901. }
  902. //---------------------------------------------------------------------------
  903. TCipher __fastcall TSecureShell::GetSCCipher()
  904. {
  905. CheckConnection();
  906. if (FSCCipher == cipWarn)
  907. {
  908. if (SshVersion == 1)
  909. {
  910. FSCCipher = FuncToSsh1Cipher(get_cipher(FBackendHandle));
  911. }
  912. else
  913. {
  914. FSCCipher = FuncToSsh2Cipher(get_sccipher(FBackendHandle));
  915. }
  916. }
  917. return FSCCipher;
  918. }
  919. //---------------------------------------------------------------------------
  920. void __fastcall TSecureShell::DoShowExtendedException(Exception * E)
  921. {
  922. DoHandleExtendedException(E);
  923. if (OnShowExtendedException != NULL)
  924. {
  925. OnShowExtendedException(this, E, NULL);
  926. }
  927. }
  928. //---------------------------------------------------------------------------
  929. void __fastcall TSecureShell::DoHandleExtendedException(Exception * E)
  930. {
  931. Log->AddException(E);
  932. }
  933. //---------------------------------------------------------------------------
  934. int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
  935. TStrings * MoreMessages, int Answers, const TQueryParams * Params, TQueryType Type)
  936. {
  937. LogEvent(FORMAT("Asking user:\n%s (%s)", (Query, (MoreMessages ? MoreMessages->CommaText : AnsiString() ))));
  938. int Answer = qaCancel;
  939. if (FOnQueryUser)
  940. {
  941. FOnQueryUser(this, Query, MoreMessages, Answers, Params, Answer, Type, NULL);
  942. }
  943. return Answer;
  944. }
  945. //---------------------------------------------------------------------------
  946. int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
  947. const AnsiString OtherMessage, int Answers, const TQueryParams * Params,
  948. TQueryType Type)
  949. {
  950. TStrings * MoreMessages = new TStringList();
  951. Integer Result;
  952. try {
  953. if (!OtherMessage.IsEmpty()) MoreMessages->Add(OtherMessage);
  954. Result = DoQueryUser(Query, MoreMessages, Answers, Params, Type);
  955. } __finally {
  956. delete MoreMessages;
  957. }
  958. return Result;
  959. }
  960. //---------------------------------------------------------------------------
  961. int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
  962. int Answers, const TQueryParams * Params, TQueryType Type)
  963. {
  964. return DoQueryUser(Query, "", Answers, Params, Type);
  965. }
  966. //---------------------------------------------------------------------------
  967. int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
  968. Exception * E, int Answers, const TQueryParams * Params, TQueryType Type)
  969. {
  970. int Result;
  971. TStrings * MoreMessages = new TStringList();
  972. try
  973. {
  974. if (E->InheritsFrom(__classid(ExtException)) &&
  975. ((ExtException*)E)->MoreMessages)
  976. {
  977. MoreMessages->Assign(((ExtException*)E)->MoreMessages);
  978. }
  979. if (!E->Message.IsEmpty() && !Query.IsEmpty()) MoreMessages->Insert(0, E->Message);
  980. Result = DoQueryUser(!Query.IsEmpty() ? Query : E->Message,
  981. MoreMessages->Count ? MoreMessages : NULL,
  982. Answers, Params, Type);
  983. }
  984. __finally
  985. {
  986. delete MoreMessages;
  987. }
  988. return Result;
  989. }
  990. //---------------------------------------------------------------------------
  991. void __fastcall TSecureShell::VerifyHostKey(const AnsiString Host, int Port,
  992. const AnsiString KeyType, const AnsiString KeyStr, const AnsiString Fingerprint)
  993. {
  994. GotHostKey();
  995. int Result;
  996. FHostKeyFingerprint = Fingerprint;
  997. // Verify the key against the registry.
  998. Result = verify_host_key(Host.c_str(), Port, KeyType.c_str(), KeyStr.c_str());
  999. if (Result != 0)
  1000. {
  1001. TQueryParams Params;
  1002. Params.HelpKeyword = (Result == 1 ? HELP_UNKNOWN_KEY : HELP_DIFFERENT_KEY);
  1003. int R = DoQueryUser(
  1004. FMTLOAD((Result == 1 ? UNKNOWN_KEY2 : DIFFERENT_KEY2), (KeyType, Fingerprint)),
  1005. qaYes | qaNo | qaCancel, &Params, qtWarning);
  1006. switch (R) {
  1007. case qaYes:
  1008. store_host_key(Host.c_str(), Port, KeyType.c_str(), KeyStr.c_str());
  1009. break;
  1010. case qaCancel:
  1011. SSH_FATAL_ERROR(LoadStr(KEY_NOT_VERIFIED));
  1012. }
  1013. }
  1014. }
  1015. //---------------------------------------------------------------------------
  1016. void __fastcall TSecureShell::AskAlg(const AnsiString AlgType,
  1017. const AnsiString AlgName)
  1018. {
  1019. AnsiString Msg;
  1020. if (AlgType == "key-exchange algorithm")
  1021. {
  1022. Msg = FMTLOAD(KEX_BELOW_TRESHOLD, (AlgName));
  1023. }
  1024. else
  1025. {
  1026. int CipherType;
  1027. if (AlgType == "cipher")
  1028. {
  1029. CipherType = CIPHER_TYPE_BOTH;
  1030. }
  1031. else if (AlgType == "client-to-server cipher")
  1032. {
  1033. CipherType = CIPHER_TYPE_CS;
  1034. }
  1035. else if (AlgType == "server-to-client cipher")
  1036. {
  1037. CipherType = CIPHER_TYPE_SC;
  1038. }
  1039. else
  1040. {
  1041. assert(false);
  1042. }
  1043. Msg = FMTLOAD(CIPHER_BELOW_TRESHOLD, (LoadStr(CipherType), AlgName));
  1044. }
  1045. if (DoQueryUser(Msg, qaYes | qaNo, NULL, qtWarning) == qaNo)
  1046. {
  1047. Abort();
  1048. }
  1049. }
  1050. //---------------------------------------------------------------------------
  1051. void __fastcall TSecureShell::OldKeyfileWarning()
  1052. {
  1053. DoQueryUser(LoadStr(OLD_KEY), qaOK, NULL, qtWarning);
  1054. }
  1055. //---------------------------------------------------------------------------
  1056. int __fastcall TSecureShell::GetStatus() const
  1057. {
  1058. return FStatus;
  1059. }
  1060. //---------------------------------------------------------------------------
  1061. void __fastcall TSecureShell::UpdateStatus(int Value)
  1062. {
  1063. if (FStatus != Value)
  1064. {
  1065. FStatus = Value;
  1066. if (FStatus > FReachedStatus) FReachedStatus = FStatus;
  1067. if (FOnUpdateStatus) FOnUpdateStatus(this);
  1068. }
  1069. }
  1070. //---------------------------------------------------------------------------
  1071. void __fastcall TSecureShell::SetUserObject(TObject * value)
  1072. {
  1073. if (UserObject != value)
  1074. {
  1075. if (UserObject)
  1076. {
  1077. delete UserObject;
  1078. }
  1079. FUserObject = value;
  1080. }
  1081. }
  1082. //=== TSessionLog -----------------------------------------------------------
  1083. const char *LogLineMarks = "<>!.*";
  1084. __fastcall TSessionLog::TSessionLog(TSecureShell * AOwner): TStringList()
  1085. {
  1086. FEnabled = true;
  1087. FOwner = AOwner;
  1088. FFile = NULL;
  1089. FLoggedLines = 0;
  1090. FTopIndex = -1;
  1091. FFileName = "";
  1092. FId = 0;
  1093. }
  1094. //---------------------------------------------------------------------------
  1095. __fastcall TSessionLog::~TSessionLog()
  1096. {
  1097. CloseLogFile();
  1098. }
  1099. //---------------------------------------------------------------------------
  1100. AnsiString __fastcall TSessionLog::GetSessionName()
  1101. {
  1102. assert(FOwner != NULL);
  1103. assert(FOwner->SessionData != NULL);
  1104. return FOwner->SessionData->SessionName;
  1105. }
  1106. //---------------------------------------------------------------------------
  1107. void __fastcall TSessionLog::SetLine(Integer Index, AnsiString value)
  1108. {
  1109. Strings[Index] = AnsiString(Strings[Index - FTopIndex][1]) + value;
  1110. }
  1111. //---------------------------------------------------------------------------
  1112. AnsiString __fastcall TSessionLog::GetLine(Integer Index)
  1113. {
  1114. return Strings[Index - FTopIndex].SubString(2, Strings[Index - FTopIndex].Length() - 1);
  1115. }
  1116. //---------------------------------------------------------------------------
  1117. void __fastcall TSessionLog::SetType(Integer Index, TLogLineType value)
  1118. {
  1119. Strings[Index - FTopIndex][1] = LogLineMarks[value];
  1120. }
  1121. //---------------------------------------------------------------------------
  1122. TLogLineType __fastcall TSessionLog::GetType(Integer Index)
  1123. {
  1124. int P = AnsiString(LogLineMarks).Pos(Strings[Index - FTopIndex][1]);
  1125. if (P)
  1126. {
  1127. return TLogLineType(P - 1);
  1128. }
  1129. else
  1130. {
  1131. assert(false);
  1132. return (TLogLineType)0;
  1133. }
  1134. }
  1135. //---------------------------------------------------------------------------
  1136. void __fastcall TSessionLog::DoAdd(TLogLineType aType, AnsiString aLine)
  1137. {
  1138. assert(Configuration);
  1139. if (IsLogging())
  1140. {
  1141. try
  1142. {
  1143. if (Configuration->Logging)
  1144. {
  1145. BeginUpdate();
  1146. }
  1147. try
  1148. {
  1149. while (!aLine.IsEmpty())
  1150. {
  1151. AnsiString MarkStr = LogLineMarks[aType];
  1152. AnsiString NewStr = CutToChar(aLine, '\n', False);
  1153. if (Configuration->Logging)
  1154. {
  1155. TStringList::Add(MarkStr + NewStr);
  1156. FLoggedLines++;
  1157. }
  1158. DoAddLine(aType, NewStr);
  1159. if (Configuration->Logging && Configuration->LogToFile)
  1160. {
  1161. if (!FFile) OpenLogFile();
  1162. if (FFile)
  1163. {
  1164. AnsiString Timestamp = FormatDateTime(" yyyy-mm-dd hh:nn:ss.zzz ", Now());
  1165. fprintf((FILE *)FFile, "%s\n",
  1166. (MarkStr + Timestamp + NewStr).c_str());
  1167. }
  1168. }
  1169. }
  1170. if (Configuration->Logging)
  1171. {
  1172. if (FTopIndex < 0) FTopIndex = 0;
  1173. DeleteUnnecessary();
  1174. }
  1175. }
  1176. __finally
  1177. {
  1178. if (Configuration->Logging)
  1179. {
  1180. EndUpdate();
  1181. }
  1182. }
  1183. }
  1184. catch (Exception &E)
  1185. {
  1186. // We failed logging, turn it of and notify user.
  1187. Configuration->Logging = False;
  1188. try
  1189. {
  1190. throw ExtException(&E, LOG_ERROR);
  1191. }
  1192. catch (Exception &E)
  1193. {
  1194. FOwner->DoShowExtendedException(&E);
  1195. }
  1196. }
  1197. }
  1198. }
  1199. //---------------------------------------------------------------------------
  1200. void __fastcall TSessionLog::Add(TLogLineType aType, AnsiString aLine)
  1201. {
  1202. DoAdd(aType, aLine);
  1203. }
  1204. //---------------------------------------------------------------------------
  1205. void __fastcall TSessionLog::AddFromOtherLog(TObject * /*Sender*/,
  1206. TLogLineType aType, const AnsiString AddedLine)
  1207. {
  1208. DoAdd(aType, AddedLine);
  1209. }
  1210. //---------------------------------------------------------------------------
  1211. void __fastcall TSessionLog::AddException(Exception * E)
  1212. {
  1213. if (E)
  1214. {
  1215. Add(llException, ExceptionLogString(E));
  1216. }
  1217. }
  1218. //---------------------------------------------------------------------------
  1219. void __fastcall TSessionLog::SetConfiguration(TConfiguration * value)
  1220. {
  1221. if (FConfiguration != value)
  1222. {
  1223. FConfiguration = value;
  1224. ReflectSettings();
  1225. }
  1226. }
  1227. //---------------------------------------------------------------------------
  1228. void __fastcall TSessionLog::ReflectSettings()
  1229. {
  1230. // if logging to file was turned off or log file was change -> close current log file
  1231. if (FFile && (!LogToFile || (FFileName != LogFileName))) CloseLogFile();
  1232. // we used to open log file here, now it is posponed until first call to DoAdd()
  1233. // this allows TSecureShell to change the Id sooner.
  1234. DeleteUnnecessary();
  1235. }
  1236. //---------------------------------------------------------------------------
  1237. void __fastcall TSessionLog::SetEnabled(bool value)
  1238. {
  1239. if (FEnabled != value)
  1240. {
  1241. FEnabled = value;
  1242. ReflectSettings();
  1243. }
  1244. }
  1245. //---------------------------------------------------------------------------
  1246. Boolean __fastcall TSessionLog::GetLogToFile()
  1247. {
  1248. return Enabled && (Configuration != NULL) && Configuration->Logging &&
  1249. Configuration->LogToFile;
  1250. }
  1251. //---------------------------------------------------------------------------
  1252. void __fastcall TSessionLog::CloseLogFile()
  1253. {
  1254. if (FFile)
  1255. {
  1256. fclose((FILE *)FFile);
  1257. FFile = NULL;
  1258. }
  1259. FFileName = "";
  1260. }
  1261. //---------------------------------------------------------------------------
  1262. void TSessionLog::OpenLogFile()
  1263. {
  1264. if (LogToFile)
  1265. {
  1266. try
  1267. {
  1268. assert(!FFile);
  1269. assert(Configuration);
  1270. AnsiString NewFileName = LogFileName;
  1271. if (Id != 0)
  1272. {
  1273. NewFileName = FORMAT("%s%s.%.8x%s", (ExtractFilePath(NewFileName),
  1274. ExtractFileName(NewFileName), static_cast<int>(FId), ExtractFileExt(NewFileName)));
  1275. }
  1276. FFile = fopen(NewFileName.c_str(), (Configuration->LogFileAppend ? "a" : "w"));
  1277. if (FFile)
  1278. {
  1279. setvbuf((FILE *)FFile, NULL, _IONBF, BUFSIZ);
  1280. FFileName = NewFileName;
  1281. }
  1282. else
  1283. {
  1284. throw Exception(FMTLOAD(LOG_OPENERROR, (NewFileName)));
  1285. }
  1286. }
  1287. catch (Exception &E)
  1288. {
  1289. // We failed logging to file, turn it of and notify user.
  1290. FFileName = "";
  1291. Configuration->LogToFile = false;
  1292. try
  1293. {
  1294. throw ExtException(&E, LOG_ERROR);
  1295. }
  1296. catch (Exception &E)
  1297. {
  1298. FOwner->DoShowExtendedException(&E);
  1299. }
  1300. }
  1301. }
  1302. }
  1303. //---------------------------------------------------------------------------
  1304. void TSessionLog::DeleteUnnecessary()
  1305. {
  1306. BeginUpdate();
  1307. try
  1308. {
  1309. if (!Configuration || !Configuration->Logging)
  1310. {
  1311. Clear();
  1312. }
  1313. else
  1314. {
  1315. while (!Configuration->LogWindowComplete && (Count > Configuration->LogWindowLines))
  1316. {
  1317. Delete(0);
  1318. FTopIndex++;
  1319. }
  1320. }
  1321. }
  1322. __finally
  1323. {
  1324. EndUpdate();
  1325. }
  1326. }
  1327. //---------------------------------------------------------------------------
  1328. TColor __fastcall TSessionLog::GetColor(int Index)
  1329. {
  1330. return LogLineColors[Type[Index]];
  1331. }
  1332. //---------------------------------------------------------------------------
  1333. void __fastcall TSessionLog::DoAddLine(TLogLineType Type, const AnsiString AddedLine)
  1334. {
  1335. if (FOnAddLine)
  1336. {
  1337. FOnAddLine(this, Type, AddedLine);
  1338. }
  1339. }
  1340. //---------------------------------------------------------------------------
  1341. void __fastcall TSessionLog::AddStartupInfo()
  1342. {
  1343. assert(FOwner != NULL);
  1344. TSessionData * Data = FOwner->SessionData;
  1345. assert(Data != NULL);
  1346. assert(Configuration);
  1347. if (Configuration->Logging || FOnAddLine)
  1348. {
  1349. BeginUpdate();
  1350. try
  1351. {
  1352. #define ADF(S, F) Add(llMessage, FORMAT(S, F));
  1353. AddSeparator();
  1354. ADF("WinSCP %s (OS %s)", (Configuration->VersionStr, Configuration->OSVersionStr));
  1355. ADF("Login time: %s", (FormatDateTime("dddddd tt", Now())));
  1356. AddSeparator();
  1357. ADF("Session name: %s", (Data->SessionName));
  1358. ADF("Host name: %s (Port: %d)", (Data->HostName, Data->PortNumber));
  1359. ADF("User name: %s (Password: %s, Key file: %s)",
  1360. (Data->UserName, BooleanToEngStr(!Data->Password.IsEmpty()),
  1361. BooleanToEngStr(!Data->PublicKeyFile.IsEmpty())))
  1362. ADF("Transfer Protocol: %s", (Data->FSProtocolStr));
  1363. ADF("SSH protocol version: %s; Compression: %s",
  1364. (Data->SshProtStr, BooleanToEngStr(Data->Compression)));
  1365. ADF("Agent forwarding: %s; TIS/CryptoCard: %s; KI: %s; GSSAPI: %s",
  1366. (BooleanToEngStr(Data->AgentFwd), BooleanToEngStr(Data->AuthTIS),
  1367. BooleanToEngStr(Data->AuthKI), BooleanToEngStr(Data->AuthGSSAPI)));
  1368. ADF("Ciphers: %s; Ssh2DES: %s",
  1369. (Data->CipherList, BooleanToEngStr(Data->Ssh2DES)));
  1370. char * PingTypes = "-NC";
  1371. ADF("Ping type: %s, Ping interval: %d sec; Timeout: %d sec",
  1372. (AnsiString(PingTypes[Data->PingType]), Data->PingInterval, Data->Timeout));
  1373. AnsiString Bugs;
  1374. char const * BugFlags = "A+-";
  1375. for (int Index = 0; Index < BUG_COUNT; Index++)
  1376. {
  1377. Bugs += AnsiString(BugFlags[Data->Bug[(TSshBug)Index]])+(Index<BUG_COUNT-1?",":"");
  1378. }
  1379. ADF("SSH Bugs: %s", (Bugs));
  1380. Bugs = "";
  1381. for (int Index = 0; Index < SFTP_BUG_COUNT; Index++)
  1382. {
  1383. Bugs += AnsiString(BugFlags[Data->SFTPBug[(TSftpBug)Index]])+(Index<SFTP_BUG_COUNT-1?",":"");
  1384. }
  1385. ADF("SFTP Bugs: %s", (Bugs));
  1386. ADF("Proxy: %s", (ProxyMethodList[Data->ProxyMethod]));
  1387. if (Data->ProxyMethod != pmNone)
  1388. {
  1389. ADF("HostName: %s (Port: %d); Username: %s; Passwd: %s",
  1390. (Data->ProxyHost, Data->ProxyPort,
  1391. Data->ProxyUsername, BooleanToEngStr(!Data->ProxyPassword.IsEmpty())));
  1392. if (Data->ProxyMethod == pmTelnet)
  1393. {
  1394. ADF("Telnet command: %s", (Data->ProxyTelnetCommand));
  1395. }
  1396. }
  1397. ADF("Return code variable: %s; Lookup user groups: %s",
  1398. ((Data->DetectReturnVar ? AnsiString("Autodetect") : Data->ReturnVar),
  1399. BooleanToEngStr(Data->LookupUserGroups)));
  1400. ADF("Shell: %s, EOL: %d", ((Data->Shell.IsEmpty()? AnsiString("default") : Data->Shell), Data->EOLType));
  1401. ADF("Local directory: %s, Remote directory: %s, Update: %s, Cache: %s",
  1402. ((Data->LocalDirectory.IsEmpty() ? AnsiString("default") : Data->LocalDirectory),
  1403. (Data->RemoteDirectory.IsEmpty() ? AnsiString("home") : Data->RemoteDirectory),
  1404. BooleanToEngStr(Data->UpdateDirectories),
  1405. BooleanToEngStr(Data->CacheDirectories)));
  1406. ADF("Cache directory changes: %s, Permanent: %s",
  1407. (BooleanToEngStr(Data->CacheDirectoryChanges),
  1408. BooleanToEngStr(Data->PreserveDirectoryChanges)));
  1409. ADF("Clear aliases: %s, Unset nat.vars: %s, Resolve symlinks: %s",
  1410. (BooleanToEngStr(Data->ClearAliases), BooleanToEngStr(Data->UnsetNationalVars),
  1411. BooleanToEngStr(Data->ResolveSymlinks)));
  1412. ADF("Alias LS: %s, Ign LS warn: %s, Scp1 Comp: %s",
  1413. (BooleanToEngStr(Data->AliasGroupList),
  1414. BooleanToEngStr(Data->IgnoreLsWarnings),
  1415. BooleanToEngStr(Data->Scp1Compatibility)));
  1416. AddSeparator();
  1417. #undef ADF
  1418. }
  1419. __finally
  1420. {
  1421. EndUpdate();
  1422. }
  1423. }
  1424. }
  1425. //---------------------------------------------------------------------------
  1426. void __fastcall TSessionLog::AddSeparator()
  1427. {
  1428. Add(llMessage, "--------------------------------------------------------------------------");
  1429. }
  1430. //---------------------------------------------------------------------------
  1431. int __fastcall TSessionLog::GetIndexes(int Index)
  1432. {
  1433. assert((Index >= 0) && (Index < Count));
  1434. int Result = TopIndex + Index;
  1435. assert((Result >= 0) && (Result < LoggedLines));
  1436. return Result;
  1437. }
  1438. //---------------------------------------------------------------------------
  1439. int __fastcall TSessionLog::GetBottomIndex()
  1440. {
  1441. return (Count ? Indexes[Count-1] : -1);
  1442. }
  1443. //---------------------------------------------------------------------------
  1444. AnsiString __fastcall TSessionLog::GetLogFileName()
  1445. {
  1446. assert(Configuration);
  1447. AnsiString Result = StripPathQuotes(Configuration->LogFileName);
  1448. return Result;
  1449. }
  1450. //---------------------------------------------------------------------------
  1451. Boolean __fastcall TSessionLog::GetLoggingToFile()
  1452. {
  1453. return
  1454. (Configuration && Configuration->Logging && Configuration->LogToFile && FFile);
  1455. }
  1456. //---------------------------------------------------------------------------
  1457. void __fastcall TSessionLog::Clear()
  1458. {
  1459. FTopIndex += Count;
  1460. TStringList::Clear();
  1461. }