SecureShell.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218
  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 "Common.h"
  11. #ifndef AUTO_WINSOCK
  12. #include <winsock2.h>
  13. #endif
  14. //---------------------------------------------------------------------------
  15. #pragma package(smart_init)
  16. //---------------------------------------------------------------------------
  17. #define MAX_BUFSIZE 16384
  18. const TColor LogLineColors[] =
  19. {clGreen, clRed, clMaroon, clBlue, clGray};
  20. //---------------------------------------------------------------------------
  21. __fastcall TSecureShell::TSecureShell()
  22. {
  23. FSessionData = new TSessionData("");
  24. FActive = False;
  25. PendLen = 0;
  26. PendSize = 0;
  27. FStdErrorTemp = "";
  28. FBytesSent = 0;
  29. FBytesReceived = 0;
  30. FLog = new TSessionLog();
  31. FOnQueryUser = NULL;
  32. FOnUpdateStatus = NULL;
  33. FOnClose = NULL;
  34. FCSCipher = cipWarn; // = not detected yet
  35. FSCCipher = cipWarn;
  36. FReachedStatus = 0;
  37. UpdateStatus(sshClosed);
  38. FConfig = new Config();
  39. FSocket = new SOCKET;
  40. FMaxPacketSize = 0;
  41. }
  42. //---------------------------------------------------------------------------
  43. __fastcall TSecureShell::~TSecureShell()
  44. {
  45. try
  46. {
  47. if (FReachedStatus)
  48. {
  49. SessionsCount--;
  50. if (SessionsCount == 0)
  51. {
  52. NetFinalize();
  53. }
  54. }
  55. ClearStdError();
  56. Active = false;
  57. SAFE_DESTROY(FSessionData);
  58. SAFE_DESTROY(FLog);
  59. delete FConfig;
  60. FConfig = NULL;
  61. UserObject = NULL;
  62. delete FSocket;
  63. FSocket = NULL;
  64. }
  65. __finally
  66. {
  67. if (CurrentSSH == this)
  68. {
  69. CurrentSSH = NULL;
  70. }
  71. }
  72. }
  73. //---------------------------------------------------------------------------
  74. void __fastcall TSecureShell::Open()
  75. {
  76. const char * InitError;
  77. char * RealHost;
  78. FReachedStatus = 0;
  79. SessionsCount++;
  80. if (SessionsCount == 1)
  81. {
  82. UpdateStatus(sshInitWinSock);
  83. NetInitialize();
  84. }
  85. Log->AddStartupInfo();
  86. Active = false;
  87. // Activate();
  88. FBackend = &ssh_backend;
  89. FAuthenticationLog = "";
  90. UpdateStatus(sshLookupHost);
  91. SessionData->StoreToConfig(FConfig);
  92. assert(!CurrentSSH);
  93. CurrentSSH = this;
  94. try
  95. {
  96. InitError = FBackend->init(this, &FBackendHandle, FConfig,
  97. SessionData->HostName.c_str(), SessionData->PortNumber, &RealHost, 0);
  98. if (InitError)
  99. {
  100. FatalError(InitError);
  101. }
  102. FRealHost = RealHost;
  103. UpdateStatus(sshConnect);
  104. /*FLoggingContext = log_init(this, (void *)FConfig);
  105. FBackend->provide_logctx(FBackendHandle, FLoggingContext);*/
  106. Init();
  107. }
  108. __finally
  109. {
  110. CurrentSSH = NULL;
  111. }
  112. CheckConnection(CONNECTION_FAILED);
  113. FLastDataSent = Now();
  114. FLoginTime = Now();
  115. Log->AddSeparator();
  116. UpdateStatus(sshAuthenticated);
  117. }
  118. //---------------------------------------------------------------------------
  119. void __fastcall TSecureShell::Init()
  120. {
  121. try
  122. {
  123. try
  124. {
  125. while (!FBackend->sendok(FBackendHandle))
  126. {
  127. WaitForData();
  128. }
  129. }
  130. catch(Exception & E)
  131. {
  132. if ((FReachedStatus == sshAuthenticate) && !FAuthenticationLog.IsEmpty())
  133. {
  134. FatalError(&E, FMTLOAD(AUTHENTICATION_LOG, (FAuthenticationLog)));
  135. }
  136. else
  137. {
  138. throw;
  139. }
  140. }
  141. }
  142. catch(Exception & E)
  143. {
  144. if (FReachedStatus == sshAuthenticate)
  145. {
  146. FatalError(&E, LoadStr(AUTHENTICATION_FAILED));
  147. }
  148. else
  149. {
  150. throw;
  151. }
  152. }
  153. }
  154. //---------------------------------------------------------------------------
  155. int __fastcall TSecureShell::GetPassword(AnsiString &Password)
  156. {
  157. int Result;
  158. if (SessionData->Password.Length() && !FPasswordTried)
  159. {
  160. LogEvent("Using stored password.");
  161. Result = 1;
  162. Password = SessionData->Password;
  163. }
  164. else
  165. {
  166. LogEvent("Asking user for password.");
  167. Result = GetSessionPassword(
  168. FMTLOAD(PROMPT_SESSION_PASSWORD, (SessionData->SessionName)),
  169. pkPassword, Password);
  170. }
  171. FPasswordTried = true;
  172. return Result;
  173. }
  174. //---------------------------------------------------------------------------
  175. void __fastcall TSecureShell::GotHostKey()
  176. {
  177. CurrentSSH->UpdateStatus(sshAuthenticate);
  178. }
  179. //---------------------------------------------------------------------------
  180. void __fastcall TSecureShell::FromBackend(Boolean IsStdErr, char * Data, Integer Length)
  181. {
  182. CheckConnection();
  183. // Following is taken from scp.c from_backend() and modified
  184. if (IsStdErr)
  185. {
  186. AddStdError(AnsiString(Data, Length));
  187. }
  188. else
  189. {
  190. unsigned char *p = (unsigned char *)Data;
  191. unsigned Len = (unsigned)Length;
  192. // If this is before the real session begins, raise exception.
  193. if (!OutPtr)
  194. {
  195. FatalError("Internal error: Session not yet begun.");
  196. }
  197. if ((OutLen > 0) && (Len > 0))
  198. {
  199. unsigned Used = OutLen;
  200. if (Used > Len) Used = Len;
  201. memcpy(OutPtr, p, Used);
  202. OutPtr += Used; OutLen -= Used;
  203. p += Used; Len -= Used;
  204. }
  205. if (Len > 0)
  206. {
  207. if (PendSize < PendLen + Len)
  208. {
  209. PendSize = PendLen + Len + 4096;
  210. Pending = (char *)
  211. (Pending ? srealloc(Pending, PendSize) : smalloc(PendSize));
  212. if (!Pending) FatalError("Out of memory");
  213. }
  214. memcpy(Pending + PendLen, p, Len);
  215. PendLen += Len;
  216. }
  217. }
  218. }
  219. //---------------------------------------------------------------------------
  220. Integer __fastcall TSecureShell::Receive(char * Buf, Integer Len)
  221. {
  222. CheckConnection();
  223. if (Len > 0)
  224. {
  225. // Following is taken from scp.c ssh_scp_recv() and modified
  226. OutPtr = Buf;
  227. OutLen = Len;
  228. /*
  229. * See if the pending-input block contains some of what we
  230. * need.
  231. */
  232. if (PendLen > 0)
  233. {
  234. unsigned PendUsed = PendLen;
  235. if (PendUsed > OutLen)
  236. {
  237. PendUsed = OutLen;
  238. }
  239. memcpy(OutPtr, Pending, PendUsed);
  240. memmove(Pending, Pending + PendUsed, PendLen - PendUsed);
  241. OutPtr += PendUsed;
  242. OutLen -= PendUsed;
  243. PendLen -= PendUsed;
  244. if (PendLen == 0)
  245. {
  246. PendSize = 0;
  247. sfree(Pending);
  248. Pending = NULL;
  249. }
  250. }
  251. // I don't undestand this yet, but it works :-)
  252. while (OutLen > 0) WaitForData();
  253. // This seems ambiguous
  254. if (Len <= 0) FatalError(LoadStr(LOST_CONNECTION));
  255. };
  256. FBytesReceived += Len;
  257. return Len;
  258. }
  259. //---------------------------------------------------------------------------
  260. AnsiString __fastcall TSecureShell::ReceiveLine()
  261. {
  262. unsigned Index;
  263. Char Ch;
  264. AnsiString Line;
  265. Boolean EOL = False;
  266. do
  267. {
  268. // If there is any buffer of received chars
  269. if (PendLen > 0)
  270. {
  271. Index = 0;
  272. // Repeat until we walk thru whole buffer or reach end-of-line
  273. while ((Index < PendLen) && (!Index || (Pending[Index-1] != '\n')))
  274. {
  275. Index++;
  276. }
  277. EOL = (Boolean)(Index && (Pending[Index-1] == '\n'));
  278. Integer PrevLen = Line.Length();
  279. Line.SetLength(PrevLen + Index);
  280. Receive(Line.c_str() + PrevLen, Index);
  281. }
  282. // If buffer don't contain end-of-line character
  283. // we read one more which causes receiving new buffer of chars
  284. if (!EOL)
  285. {
  286. Receive(&Ch, 1);
  287. Line += Ch;
  288. EOL = (Ch == '\n');
  289. }
  290. }
  291. while (!EOL);
  292. // We don't want end-of-line character
  293. Line.SetLength(Line.Length()-1);
  294. Log->Add(llOutput, Line);
  295. return Line;
  296. }
  297. //---------------------------------------------------------------------------
  298. void __fastcall TSecureShell::SendSpecial(int Code)
  299. {
  300. LogEvent(FORMAT("Sending special code: %d", (Code)));
  301. CheckConnection();
  302. FBackend->special(FBackendHandle, (Telnet_Special)Code);
  303. FLastDataSent = Now();
  304. }
  305. //---------------------------------------------------------------------------
  306. void __fastcall TSecureShell::SendEOF()
  307. {
  308. SendSpecial(TS_EOF);
  309. }
  310. //---------------------------------------------------------------------------
  311. void __fastcall TSecureShell::Send(const char * Buf, Integer Len)
  312. {
  313. CheckConnection();
  314. int BufSize = FBackend->send(FBackendHandle, (char *)Buf, Len);
  315. FLastDataSent = Now();
  316. FBytesSent += Len;
  317. while (BufSize > MAX_BUFSIZE)
  318. {
  319. WaitForData();
  320. BufSize = FBackend->sendbuffer(FBackendHandle);
  321. }
  322. }
  323. //---------------------------------------------------------------------------
  324. void __fastcall TSecureShell::SendNull()
  325. {
  326. LogEvent("Sending NULL.");
  327. Send("", 1);
  328. }
  329. //---------------------------------------------------------------------------
  330. void __fastcall TSecureShell::SendStr(AnsiString Str)
  331. {
  332. CheckConnection();
  333. Send(Str.c_str(), Str.Length());
  334. }
  335. //---------------------------------------------------------------------------
  336. void __fastcall TSecureShell::SendLine(AnsiString Line)
  337. {
  338. SendStr(Line);
  339. Send("\n", 1);
  340. Log->Add(llInput, Line);
  341. }
  342. //---------------------------------------------------------------------------
  343. void __fastcall TSecureShell::AddStdError(AnsiString Str)
  344. {
  345. StdError += Str;
  346. Integer P;
  347. Str = DeleteChar(Str, '\r');
  348. // We send only whole line at once to log, so we have to cache
  349. // incoming std error data
  350. FStdErrorTemp += Str;
  351. AnsiString Line;
  352. // Do we have at least one complete line in std error cache?
  353. while ((P = FStdErrorTemp.Pos("\n")) > 0)
  354. {
  355. Line = FStdErrorTemp.SubString(1, P-1);
  356. FStdErrorTemp.Delete(1, P);
  357. if (Status == sshAuthenticate)
  358. {
  359. FAuthenticationLog += (FAuthenticationLog.IsEmpty() ? "" : "\n") + Line;
  360. }
  361. Log->Add(llStdError, Line);
  362. }
  363. }
  364. //---------------------------------------------------------------------------
  365. void __fastcall TSecureShell::ClearStdError()
  366. {
  367. // Flush std error cache
  368. if (!FStdErrorTemp.IsEmpty())
  369. {
  370. FAuthenticationLog +=
  371. (FAuthenticationLog.IsEmpty() ? "" : "\n") + FStdErrorTemp;
  372. Log->Add(llStdError, FStdErrorTemp);
  373. FStdErrorTemp = "";
  374. }
  375. StdError = "";
  376. }
  377. //---------------------------------------------------------------------------
  378. void __fastcall TSecureShell::FatalError(Exception * E, AnsiString Msg)
  379. {
  380. if (FActive)
  381. {
  382. // We log this instead of exception handler, because Close() would
  383. // probably cause exception handler to loose pointer to TShellLog()
  384. LogEvent("Attempt to close connection due to fatal exception:");
  385. Log->Add(llException, Msg);
  386. Log->AddException(E);
  387. Close();
  388. }
  389. SSH_FATAL_ERROR_EXT(E, Msg);
  390. }
  391. //---------------------------------------------------------------------------
  392. void __fastcall TSecureShell::FatalError(AnsiString Error)
  393. {
  394. FatalError(NULL, Error);
  395. }
  396. //---------------------------------------------------------------------------
  397. void __fastcall TSecureShell::SetSocket(void * value)
  398. {
  399. assert(value);
  400. if (FActive && (*static_cast<SOCKET*>(value) != INVALID_SOCKET))
  401. FatalError("Cannot set socket during connection");
  402. assert(FSocket);
  403. *static_cast<SOCKET*>(FSocket) = *static_cast<SOCKET*>(value);
  404. if (*static_cast<SOCKET*>(FSocket) != INVALID_SOCKET)
  405. {
  406. FActive = true;
  407. }
  408. else
  409. {
  410. Discard();
  411. }
  412. }
  413. //---------------------------------------------------------------------------
  414. void __fastcall TSecureShell::SetSessionData(TSessionData * value)
  415. {
  416. assert(!FActive);
  417. FSessionData->Assign(value);
  418. if (FLog)
  419. {
  420. FLog->Data = FSessionData;
  421. }
  422. }
  423. //---------------------------------------------------------------------------
  424. void __fastcall TSecureShell::SetActive(bool value)
  425. {
  426. if (FActive != value)
  427. {
  428. if (value)
  429. {
  430. Open();
  431. }
  432. else
  433. {
  434. Close();
  435. }
  436. }
  437. }
  438. //---------------------------------------------------------------------------
  439. bool __fastcall TSecureShell::GetActive() const
  440. {
  441. return FActive;
  442. }
  443. //---------------------------------------------------------------------------
  444. void __fastcall TSecureShell::Discard()
  445. {
  446. bool WasActive = FActive;
  447. FActive = false;
  448. if (WasActive && OnClose)
  449. {
  450. OnClose(this);
  451. }
  452. FStatus = sshClosed;
  453. }
  454. //---------------------------------------------------------------------------
  455. void __fastcall TSecureShell::Close()
  456. {
  457. LogEvent("Closing connection.");
  458. CheckConnection();
  459. assert(!CurrentSSH || CurrentSSH == this);
  460. TSecureShell * PCurrentSSH = CurrentSSH;
  461. CurrentSSH = this;
  462. try
  463. {
  464. ssh_close(FBackendHandle);
  465. // This should be called insted, but there seem tu be error in freeing
  466. // FBackend->free(FBackendHandle);
  467. }
  468. __finally
  469. {
  470. CurrentSSH = PCurrentSSH;
  471. }
  472. Discard();
  473. }
  474. //---------------------------------------------------------------------------
  475. void inline __fastcall TSecureShell::CheckConnection(int Message)
  476. {
  477. if (!FActive || get_ssh_state_closed(FBackendHandle))
  478. {
  479. AnsiString Str = LoadStr(Message >= 0 ? Message : NOT_CONNECTED);
  480. int ExitCode = get_ssh_exitcode(FBackendHandle);
  481. if (ExitCode >= 0)
  482. {
  483. Str += " " + FMTLOAD(SSH_EXITCODE, (ExitCode));
  484. }
  485. FatalError(Str);
  486. }
  487. }
  488. //---------------------------------------------------------------------------
  489. extern int select_result(WPARAM, LPARAM);
  490. void __fastcall TSecureShell::WaitForData()
  491. {
  492. int R;
  493. do
  494. {
  495. CheckConnection();
  496. struct timeval time;
  497. fd_set readfds;
  498. FD_ZERO(&readfds);
  499. FD_SET(*static_cast<SOCKET*>(FSocket), &readfds);
  500. time.tv_sec = FSessionData->Timeout;
  501. time.tv_usec = 0;
  502. R = select(1, &readfds, NULL, NULL, &time);
  503. if (R < 0)
  504. {
  505. SSH_FATAL_ERROR(
  506. Format("Cannot determine status of socket (%d).", ARRAYOFCONST((R))));
  507. }
  508. else if (R == 0)
  509. {
  510. LogEvent("Waiting for data timed out, asking user what to do.");
  511. if (DoQueryUser(FMTLOAD(CONFIRM_PROLONG_TIMEOUT, (FSessionData->Timeout)),
  512. qaRetry | qaAbort, qpFatalAbort | qpAllowContinueOnError) != qaRetry)
  513. {
  514. FatalError(LoadStr(USER_TERMINATED));
  515. }
  516. }
  517. }
  518. while (R <= 0);
  519. assert(!CurrentSSH || CurrentSSH == this);
  520. TSecureShell * PrevSSH = CurrentSSH;
  521. CurrentSSH = this;
  522. try
  523. {
  524. select_result((WPARAM)(*static_cast<SOCKET*>(FSocket)), (LPARAM)FD_READ);
  525. }
  526. __finally
  527. {
  528. CurrentSSH = PrevSSH;
  529. }
  530. }
  531. //---------------------------------------------------------------------------
  532. bool __fastcall TSecureShell::SshFallbackCmd() const
  533. {
  534. return ssh_fallback_cmd(FBackendHandle);
  535. }
  536. //---------------------------------------------------------------------------
  537. void __fastcall TSecureShell::Error(const AnsiString Error) const
  538. {
  539. SSH_ERROR(Error);
  540. }
  541. //---------------------------------------------------------------------------
  542. void __fastcall TSecureShell::Idle()
  543. {
  544. // In Putty this is called each 100 ms when in foreground and
  545. // each 10 min when in background
  546. noise_regular();
  547. // Keep session alive
  548. if ((FSessionData->PingType != ptOff) &&
  549. (Now() - FLastDataSent > FSessionData->PingIntervalDT))
  550. {
  551. KeepAlive();
  552. // in case keep alive could not be processed, postpone next attempt
  553. FLastDataSent = Now();
  554. }
  555. }
  556. //---------------------------------------------------------------------------
  557. void __fastcall TSecureShell::KeepAlive()
  558. {
  559. LogEvent("Sending null packet to keep session alive.");
  560. SendSpecial(TS_PING);
  561. }
  562. //---------------------------------------------------------------------------
  563. void __fastcall TSecureShell::SetLog(TSessionLog * value)
  564. {
  565. FLog->Assign(value);
  566. FLog->Data = SessionData;
  567. }
  568. //---------------------------------------------------------------------------
  569. void __fastcall TSecureShell::SetConfiguration(TConfiguration *value)
  570. {
  571. FConfiguration = value;
  572. Log->Configuration = value;
  573. }
  574. //---------------------------------------------------------------------------
  575. TDateTime __fastcall TSecureShell::GetDuration() const
  576. {
  577. return Now() - FLoginTime;
  578. }
  579. //---------------------------------------------------------------------------
  580. unsigned long __fastcall TSecureShell::MaxPacketSize()
  581. {
  582. if (SshVersion == 1)
  583. {
  584. return 0;
  585. }
  586. else
  587. {
  588. if (FMaxPacketSize == 0)
  589. {
  590. unsigned long RemWindow = ssh2_remwindow(FBackendHandle);
  591. FMaxPacketSize = ssh2_remmaxpkt(FBackendHandle);
  592. if (RemWindow < FMaxPacketSize)
  593. {
  594. FMaxPacketSize = RemWindow;
  595. }
  596. }
  597. return FMaxPacketSize;
  598. }
  599. }
  600. //---------------------------------------------------------------------------
  601. TCompressionType __fastcall TSecureShell::FuncToCompression(
  602. const void * Compress) const
  603. {
  604. if (SshVersion == 1)
  605. {
  606. return get_ssh1_compressing(FBackendHandle) ? ctZLib : ctNone;
  607. }
  608. else
  609. {
  610. return (ssh_compress *)Compress == &ssh_zlib ? ctZLib : ctNone;
  611. }
  612. }
  613. //---------------------------------------------------------------------------
  614. TCompressionType __fastcall TSecureShell::GetCSCompression() const
  615. {
  616. assert(Active);
  617. return FuncToCompression(get_cscomp(FBackendHandle));
  618. }
  619. //---------------------------------------------------------------------------
  620. TCompressionType __fastcall TSecureShell::GetSCCompression() const
  621. {
  622. assert(Active);
  623. return FuncToCompression(get_sccomp(FBackendHandle));
  624. }
  625. //---------------------------------------------------------------------------
  626. int __fastcall TSecureShell::GetSshVersion() const
  627. {
  628. assert(Active);
  629. return get_ssh_version(FBackendHandle);
  630. }
  631. //---------------------------------------------------------------------------
  632. TCipher __fastcall TSecureShell::FuncToSsh1Cipher(const void * Cipher) const
  633. {
  634. const ssh_cipher *CipherFuncs[] =
  635. {&ssh_3des, &ssh_des, &ssh_blowfish_ssh1};
  636. const TCipher TCiphers[] = {cip3DES, cipDES, cipBlowfish};
  637. assert(LENOF(CipherFuncs) == LENOF(TCiphers));
  638. TCipher Result = cipWarn;
  639. for (int Index = 0; Index < LENOF(TCiphers); Index++)
  640. {
  641. if ((ssh_cipher *)Cipher == CipherFuncs[Index])
  642. {
  643. Result = TCiphers[Index];
  644. }
  645. }
  646. assert(Result != cipWarn);
  647. return Result;
  648. }
  649. //---------------------------------------------------------------------------
  650. TCipher __fastcall TSecureShell::FuncToSsh2Cipher(const void * Cipher) const
  651. {
  652. const ssh2_ciphers *CipherFuncs[] =
  653. {&ssh2_3des, &ssh2_des, &ssh2_aes, &ssh2_blowfish};
  654. const TCipher TCiphers[] = {cip3DES, cipDES, cipAES, cipBlowfish};
  655. assert(LENOF(CipherFuncs) == LENOF(TCiphers));
  656. TCipher Result = cipWarn;
  657. for (int C = 0; C < LENOF(TCiphers); C++)
  658. {
  659. for (int F = 0; F < CipherFuncs[C]->nciphers; F++)
  660. {
  661. if ((ssh2_cipher *)Cipher == CipherFuncs[C]->list[F])
  662. {
  663. Result = TCiphers[C];
  664. }
  665. }
  666. }
  667. assert(Result != cipWarn);
  668. return Result;
  669. }
  670. //---------------------------------------------------------------------------
  671. TCipher __fastcall TSecureShell::GetCSCipher()
  672. {
  673. assert(Active);
  674. if (FCSCipher == cipWarn)
  675. {
  676. if (SshVersion == 1)
  677. {
  678. FCSCipher = FuncToSsh1Cipher(get_cipher(FBackendHandle));
  679. }
  680. else
  681. {
  682. FCSCipher = FuncToSsh2Cipher(get_cscipher(FBackendHandle));
  683. }
  684. }
  685. return FCSCipher;
  686. }
  687. //---------------------------------------------------------------------------
  688. TCipher __fastcall TSecureShell::GetSCCipher()
  689. {
  690. CheckConnection();
  691. if (FSCCipher == cipWarn)
  692. {
  693. if (SshVersion == 1)
  694. {
  695. FSCCipher = FuncToSsh1Cipher(get_cipher(FBackendHandle));
  696. }
  697. else
  698. {
  699. FSCCipher = FuncToSsh2Cipher(get_sccipher(FBackendHandle));
  700. }
  701. }
  702. return FSCCipher;
  703. }
  704. //---------------------------------------------------------------------------
  705. int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
  706. TStrings * MoreMessages, int Answers, int Params, TQueryType Type)
  707. {
  708. LogEvent(FORMAT("Asking user:\n%s (%s)", (Query, (MoreMessages ? MoreMessages->CommaText : AnsiString() ))));
  709. int Answer = qaCancel;
  710. if (FOnQueryUser)
  711. {
  712. FOnQueryUser(this, Query, MoreMessages, Answers, Params, Answer, Type);
  713. }
  714. return Answer;
  715. }
  716. //---------------------------------------------------------------------------
  717. int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
  718. const AnsiString OtherMessage, int Answers, int Params)
  719. {
  720. TStrings * MoreMessages = new TStringList();
  721. Integer Result;
  722. try {
  723. if (!OtherMessage.IsEmpty()) MoreMessages->Add(OtherMessage);
  724. Result = DoQueryUser(Query, MoreMessages, Answers, Params);
  725. } __finally {
  726. delete MoreMessages;
  727. }
  728. return Result;
  729. }
  730. //---------------------------------------------------------------------------
  731. int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
  732. int Answers, int Params)
  733. {
  734. return DoQueryUser(Query, "", Answers, Params);
  735. }
  736. //---------------------------------------------------------------------------
  737. int __fastcall TSecureShell::DoQueryUser(const AnsiString Query,
  738. Exception * E, int Answers, int Params)
  739. {
  740. int Result;
  741. TStrings * MoreMessages = new TStringList();
  742. try
  743. {
  744. if (E->InheritsFrom(__classid(ExtException)) &&
  745. ((ExtException*)E)->MoreMessages)
  746. {
  747. MoreMessages->Assign(((ExtException*)E)->MoreMessages);
  748. }
  749. if (!E->Message.IsEmpty() && !Query.IsEmpty()) MoreMessages->Insert(0, E->Message);
  750. Result = DoQueryUser(!Query.IsEmpty() ? Query : E->Message,
  751. MoreMessages->Count ? MoreMessages : NULL,
  752. Answers, Params);
  753. }
  754. __finally
  755. {
  756. delete MoreMessages;
  757. }
  758. return Result;
  759. }
  760. //---------------------------------------------------------------------------
  761. void __fastcall TSecureShell::VerifyHostKey(const AnsiString Host, int Port,
  762. const AnsiString KeyType, const AnsiString KeyStr, const AnsiString Fingerprint)
  763. {
  764. GotHostKey();
  765. int Result;
  766. // Verify the key against the registry.
  767. Result = verify_host_key(Host.c_str(), Port, KeyType.c_str(), KeyStr.c_str());
  768. if (Result != 0)
  769. {
  770. int R = DoQueryUser(
  771. FMTLOAD((Result == 1 ? UNKNOWN_KEY : DIFFERENT_KEY), (Fingerprint)),
  772. NULL, qaYes | qaNo | qaCancel, 0, qtWarning);
  773. switch (R) {
  774. case qaYes:
  775. store_host_key(Host.c_str(), Port, KeyType.c_str(), KeyStr.c_str());
  776. break;
  777. case qaCancel:
  778. SSH_FATAL_ERROR(LoadStr(KEY_NOT_VERIFIED));
  779. }
  780. }
  781. }
  782. //---------------------------------------------------------------------------
  783. void __fastcall TSecureShell::AskCipher(const AnsiString CipherName, int CipherType)
  784. {
  785. static int CipherTypeRes[] = {CIPHER_TYPE_BOTH, CIPHER_TYPE_CS, CIPHER_TYPE_SC};
  786. assert(CipherType >= 0 && CipherType < LENOF(CipherTypeRes));
  787. AnsiString Msg = FMTLOAD(CIPHER_BELOW_TRESHOLD,
  788. (LoadStr(CipherTypeRes[CipherType]), CipherName));
  789. if (DoQueryUser(Msg, NULL, qaYes | qaNo, 0, qtWarning) == qaNo)
  790. {
  791. Abort();
  792. }
  793. }
  794. //---------------------------------------------------------------------------
  795. void __fastcall TSecureShell::OldKeyfileWarning()
  796. {
  797. DoQueryUser(LoadStr(OLD_KEY), NULL, qaOK, 0, qtWarning);
  798. }
  799. //---------------------------------------------------------------------------
  800. int __fastcall TSecureShell::GetStatus() const
  801. {
  802. return FStatus;
  803. }
  804. //---------------------------------------------------------------------------
  805. void __fastcall TSecureShell::UpdateStatus(int Value)
  806. {
  807. if (FStatus != Value)
  808. {
  809. FStatus = Value;
  810. if (FStatus > FReachedStatus) FReachedStatus = FStatus;
  811. if (FOnUpdateStatus) FOnUpdateStatus(this);
  812. }
  813. }
  814. //---------------------------------------------------------------------------
  815. void __fastcall TSecureShell::SetUserObject(TObject * value)
  816. {
  817. if (UserObject != value)
  818. {
  819. if (UserObject)
  820. {
  821. delete UserObject;
  822. }
  823. FUserObject = value;
  824. }
  825. }
  826. //=== TSessionLog -----------------------------------------------------------
  827. const char *LogLineMarks = "<>!.*";
  828. __fastcall TSessionLog::TSessionLog(): TStringList()
  829. {
  830. FFile = NULL;
  831. FData = NULL;
  832. FLoggedLines = 0;
  833. FTopIndex = -1;
  834. FFileName = "";
  835. }
  836. //---------------------------------------------------------------------------
  837. __fastcall TSessionLog::~TSessionLog()
  838. {
  839. CloseLogFile();
  840. }
  841. //---------------------------------------------------------------------------
  842. void __fastcall TSessionLog::SetLine(Integer Index, AnsiString value)
  843. {
  844. Strings[Index] = AnsiString(Strings[Index - FTopIndex][1]) + value;
  845. }
  846. //---------------------------------------------------------------------------
  847. AnsiString __fastcall TSessionLog::GetLine(Integer Index)
  848. {
  849. return Strings[Index - FTopIndex].SubString(2, Strings[Index - FTopIndex].Length() - 1);
  850. }
  851. //---------------------------------------------------------------------------
  852. void __fastcall TSessionLog::SetType(Integer Index, TLogLineType value)
  853. {
  854. Strings[Index - FTopIndex][1] = LogLineMarks[value];
  855. }
  856. //---------------------------------------------------------------------------
  857. TLogLineType __fastcall TSessionLog::GetType(Integer Index)
  858. {
  859. int P = AnsiString(LogLineMarks).Pos(Strings[Index - FTopIndex][1]);
  860. if (P)
  861. {
  862. return TLogLineType(P - 1);
  863. }
  864. else
  865. {
  866. assert(false);
  867. return (TLogLineType)0;
  868. }
  869. }
  870. //---------------------------------------------------------------------------
  871. void __fastcall TSessionLog::Add(TLogLineType aType, AnsiString aLine)
  872. {
  873. assert(Configuration && Data);
  874. if (Configuration->Logging || FOnAddLine)
  875. {
  876. try
  877. {
  878. if (Configuration->Logging)
  879. {
  880. BeginUpdate();
  881. }
  882. try
  883. {
  884. while (!aLine.IsEmpty())
  885. {
  886. AnsiString NewStr;
  887. NewStr = AnsiString(LogLineMarks[aType]) + CutToChar(aLine, '\n', False);
  888. if (Configuration->Logging)
  889. {
  890. TStringList::Add(NewStr);
  891. FLoggedLines++;
  892. }
  893. NewStr.Insert(' ', 2);
  894. DoAddLine(NewStr);
  895. if (Configuration->Logging && Configuration->LogToFile)
  896. {
  897. if (!FFile) OpenLogFile();
  898. if (FFile) fprintf((FILE *)FFile, "%s\n", NewStr.c_str());
  899. }
  900. }
  901. if (Configuration->Logging)
  902. {
  903. if (FTopIndex < 0) FTopIndex = 0;
  904. DeleteUnnecessary();
  905. }
  906. }
  907. __finally
  908. {
  909. if (Configuration->Logging)
  910. {
  911. EndUpdate();
  912. }
  913. }
  914. }
  915. catch (Exception &E)
  916. {
  917. // We failed logging, turn it of and notify user.
  918. Configuration->Logging = False;
  919. try
  920. {
  921. throw ExtException(&E, LOG_ERROR);
  922. }
  923. catch (Exception &E)
  924. {
  925. ShowExtendedException(&E, this);
  926. }
  927. }
  928. }
  929. }
  930. //---------------------------------------------------------------------------
  931. void __fastcall TSessionLog::AddException(Exception * E)
  932. {
  933. if (E)
  934. {
  935. Add(llException, ExceptionLogString(E));
  936. }
  937. }
  938. //---------------------------------------------------------------------------
  939. void TSessionLog::SetData(TSessionData * value)
  940. {
  941. if (Data != value)
  942. {
  943. FData = value;
  944. // In TSessionData there is no longer any settings that needs to be reflected
  945. // ReflectSettings();
  946. }
  947. }
  948. //---------------------------------------------------------------------------
  949. void __fastcall TSessionLog::SetConfiguration(TConfiguration * value)
  950. {
  951. if (FConfiguration != value)
  952. {
  953. FConfiguration = value;
  954. ReflectSettings();
  955. }
  956. }
  957. //---------------------------------------------------------------------------
  958. void __fastcall TSessionLog::ReflectSettings()
  959. {
  960. // if logging to file was turned off or log file was change -> close current log file
  961. if (FFile && (!LogToFile || (FFileName != LogFileName))) CloseLogFile();
  962. // if loggin to file is enabled and we don't log -> open log file
  963. if (!FFile && LogToFile) OpenLogFile();
  964. DeleteUnnecessary();
  965. }
  966. //---------------------------------------------------------------------------
  967. Boolean __fastcall TSessionLog::GetLogToFile()
  968. {
  969. return (Configuration && Configuration->Logging && Configuration->LogToFile);
  970. }
  971. //---------------------------------------------------------------------------
  972. void __fastcall TSessionLog::CloseLogFile()
  973. {
  974. if (FFile)
  975. {
  976. fclose((FILE *)FFile);
  977. FFile = NULL;
  978. }
  979. FFileName = "";
  980. }
  981. //---------------------------------------------------------------------------
  982. void TSessionLog::OpenLogFile()
  983. {
  984. if (LogToFile)
  985. {
  986. try
  987. {
  988. assert(!FFile);
  989. assert(Configuration);
  990. FFile = fopen(LogFileName.c_str(), (Configuration->LogFileAppend ? "a" : "w"));
  991. if (FFile)
  992. {
  993. setvbuf((FILE *)FFile, NULL, _IONBF, BUFSIZ);
  994. FFileName = LogFileName;
  995. }
  996. else
  997. {
  998. throw Exception(FMTLOAD(LOG_OPENERROR, (LogFileName)));
  999. }
  1000. }
  1001. catch (Exception &E)
  1002. {
  1003. // We failed logging to file, turn it of and notify user.
  1004. FFileName = "";
  1005. Configuration->LogToFile = false;
  1006. try
  1007. {
  1008. throw ExtException(&E, LOG_ERROR);
  1009. }
  1010. catch (Exception &E)
  1011. {
  1012. ShowExtendedException(&E, this);
  1013. }
  1014. }
  1015. }
  1016. }
  1017. //---------------------------------------------------------------------------
  1018. void TSessionLog::DeleteUnnecessary()
  1019. {
  1020. BeginUpdate();
  1021. try
  1022. {
  1023. if (!Configuration || !Configuration->Logging)
  1024. {
  1025. Clear();
  1026. }
  1027. else
  1028. {
  1029. while (!Configuration->LogWindowComplete && (Count > Configuration->LogWindowLines))
  1030. {
  1031. Delete(0);
  1032. FTopIndex++;
  1033. }
  1034. }
  1035. }
  1036. __finally
  1037. {
  1038. EndUpdate();
  1039. }
  1040. }
  1041. //---------------------------------------------------------------------------
  1042. TColor __fastcall TSessionLog::GetColor(int Index)
  1043. {
  1044. return LogLineColors[Type[Index]];
  1045. }
  1046. //---------------------------------------------------------------------------
  1047. void __fastcall TSessionLog::DoAddLine(const AnsiString AddedLine)
  1048. {
  1049. if (FOnAddLine)
  1050. {
  1051. FOnAddLine(this, AddedLine);
  1052. }
  1053. }
  1054. //---------------------------------------------------------------------------
  1055. void __fastcall TSessionLog::AddStartupInfo()
  1056. {
  1057. assert(Configuration);
  1058. if (Configuration->Logging || FOnAddLine)
  1059. {
  1060. BeginUpdate();
  1061. try
  1062. {
  1063. #define ADF(S, F) Add(llMessage, FORMAT(S, F));
  1064. AddSeparator();
  1065. ADF("WinSCP %s", (Configuration->VersionStr));
  1066. ADF("Login time: %s", (FormatDateTime("dddddd tt", Now())));
  1067. AddSeparator();
  1068. ADF("Session name: %s", (Data->SessionName));
  1069. ADF("Host name: %s (Port: %d)", (Data->HostName, Data->PortNumber));
  1070. ADF("User name: %s (Password: %s, Key file: %s)",
  1071. (Data->UserName, BooleanToEngStr(!Data->Password.IsEmpty()),
  1072. BooleanToEngStr(!Data->PublicKeyFile.IsEmpty())))
  1073. ADF("Transfer Protocol: %s", (Data->FSProtocolStr));
  1074. ADF("SSH protocol version: %s; Compression: %s",
  1075. (Data->SshProtStr, BooleanToEngStr(Data->Compression)));
  1076. ADF("Agent forwarding: %s; TIS/CryptoCard: %s; KI: %s",
  1077. (BooleanToEngStr(Data->AgentFwd), BooleanToEngStr(Data->AuthTIS),
  1078. BooleanToEngStr(Data->AuthKI)));
  1079. ADF("Ciphers: %s; Ssh2DES: %s",
  1080. (Data->CipherList, BooleanToEngStr(Data->Ssh2DES)));
  1081. char * PingTypes = "-NC";
  1082. ADF("Ping type: %s, Ping interval: %d sec; Timeout: %d sec",
  1083. (AnsiString(PingTypes[Data->PingType]), Data->PingInterval, Data->Timeout));
  1084. AnsiString Bugs;
  1085. char const * BugFlags = "A+-";
  1086. for (int Index = 0; Index < BUG_COUNT; Index++)
  1087. {
  1088. Bugs += AnsiString(BugFlags[Data->Bug[(TSshBug)Index]])+(Index<BUG_COUNT?",":"");
  1089. }
  1090. ADF("SSH Bugs: %s", (Bugs));
  1091. ADF("Proxy: %s", (ProxyMethodList[Data->ProxyMethod]));
  1092. if (Data->ProxyMethod != pmNone)
  1093. {
  1094. ADF("HostName: %s (Port: %d); Username: %s; Passwd: %s",
  1095. (Data->ProxyHost, Data->ProxyPort,
  1096. Data->ProxyUsername, BooleanToEngStr(!Data->ProxyPassword.IsEmpty())));
  1097. if (Data->ProxyMethod == pmTelnet)
  1098. {
  1099. ADF("Telnet command: %s", (Data->ProxyTelnetCommand));
  1100. }
  1101. }
  1102. ADF("Return code variable: %s; Lookup user groups: %s",
  1103. ((Data->DetectReturnVar ? AnsiString("Autodetect") : Data->ReturnVar),
  1104. BooleanToEngStr(Data->LookupUserGroups)));
  1105. ADF("Shell: %s, EOL: %d", ((Data->Shell.IsEmpty()? AnsiString("default") : Data->Shell), Data->EOLType));
  1106. ADF("Local directory: %s, Remote directory: %s, Update: %s, Cache: %s",
  1107. ((Data->LocalDirectory.IsEmpty() ? AnsiString("default") : Data->LocalDirectory),
  1108. (Data->RemoteDirectory.IsEmpty() ? AnsiString("home") : Data->RemoteDirectory),
  1109. BooleanToEngStr(Data->UpdateDirectories),
  1110. BooleanToEngStr(Data->CacheDirectories)));
  1111. ADF("Cache directory changes: %s, Permanent: %s",
  1112. (BooleanToEngStr(Data->CacheDirectoryChanges),
  1113. BooleanToEngStr(Data->PreserveDirectoryChanges)));
  1114. ADF("Clear aliases: %s, Unset nat.vars: %s, Resolve symlinks: %s",
  1115. (BooleanToEngStr(Data->ClearAliases), BooleanToEngStr(Data->UnsetNationalVars),
  1116. BooleanToEngStr(Data->ResolveSymlinks)));
  1117. ADF("Alias LS: %s, Ign LS warn: %s, Scp1 Comp: %s",
  1118. (BooleanToEngStr(Data->AliasGroupList),
  1119. BooleanToEngStr(Data->IgnoreLsWarnings),
  1120. BooleanToEngStr(Data->Scp1Compatibility)));
  1121. AddSeparator();
  1122. #undef ADF
  1123. }
  1124. __finally
  1125. {
  1126. EndUpdate();
  1127. }
  1128. }
  1129. }
  1130. //---------------------------------------------------------------------------
  1131. void __fastcall TSessionLog::AddSeparator()
  1132. {
  1133. Add(llMessage, "--------------------------------------------------------------------------");
  1134. }
  1135. //---------------------------------------------------------------------------
  1136. int __fastcall TSessionLog::GetIndexes(int Index)
  1137. {
  1138. assert((Index >= 0) && (Index < Count));
  1139. int Result = TopIndex + Index;
  1140. assert((Result >= 0) && (Result < LoggedLines));
  1141. return Result;
  1142. }
  1143. //---------------------------------------------------------------------------
  1144. int __fastcall TSessionLog::GetBottomIndex()
  1145. {
  1146. return (Count ? Indexes[Count-1] : -1);
  1147. }
  1148. //---------------------------------------------------------------------------
  1149. AnsiString __fastcall TSessionLog::GetLogFileName()
  1150. {
  1151. assert(Configuration);
  1152. AnsiString Result = StripPathQuotes(Configuration->LogFileName);
  1153. return Result;
  1154. }
  1155. //---------------------------------------------------------------------------
  1156. Boolean __fastcall TSessionLog::GetLoggingToFile()
  1157. {
  1158. return
  1159. (Configuration && Configuration->Logging && Configuration->LogToFile && FFile);
  1160. }
  1161. //---------------------------------------------------------------------------
  1162. void __fastcall TSessionLog::Clear()
  1163. {
  1164. FTopIndex += Count;
  1165. TStringList::Clear();
  1166. }