SecureShell.cpp 32 KB

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