SecureShell.cpp 51 KB

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