SecureShell.cpp 52 KB

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