SecureShell.cpp 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include "PuttyIntf.h"
  5. #include "Exceptions.h"
  6. #include "Interface.h"
  7. #include "SecureShell.h"
  8. #include "TextsCore.h"
  9. #include "HelpCore.h"
  10. #include "Common.h"
  11. #include "CoreMain.h"
  12. #ifndef AUTO_WINSOCK
  13. #include <winsock2.h>
  14. #endif
  15. //---------------------------------------------------------------------------
  16. #pragma package(smart_init)
  17. //---------------------------------------------------------------------------
  18. #define MAX_BUFSIZE 32768
  19. //---------------------------------------------------------------------------
  20. struct TPuttyTranslation
  21. {
  22. const char * Original;
  23. int Translation;
  24. };
  25. //---------------------------------------------------------------------------
  26. __fastcall TSecureShell::TSecureShell(TSessionUI* UI,
  27. TSessionData * SessionData, TSessionLog * Log, TConfiguration * Configuration)
  28. {
  29. FUI = UI;
  30. FSessionData = SessionData;
  31. FLog = Log;
  32. FConfiguration = Configuration;
  33. FActive = false;
  34. FWaiting = 0;
  35. FOpened = false;
  36. OutPtr = NULL;
  37. Pending = NULL;
  38. FBackendHandle = NULL;
  39. ResetConnection();
  40. FOnCaptureOutput = NULL;
  41. FOnReceive = NULL;
  42. FConfig = new Config();
  43. memset(FConfig, 0, sizeof(*FConfig));
  44. FSocket = INVALID_SOCKET;
  45. FSocketEvent = CreateEvent(NULL, false, false, NULL);
  46. FFrozen = false;
  47. FSimple = false;
  48. }
  49. //---------------------------------------------------------------------------
  50. __fastcall TSecureShell::~TSecureShell()
  51. {
  52. assert(FWaiting == 0);
  53. Active = false;
  54. ResetConnection();
  55. CloseHandle(FSocketEvent);
  56. ClearConfig(FConfig);
  57. delete FConfig;
  58. FConfig = NULL;
  59. }
  60. //---------------------------------------------------------------------------
  61. void __fastcall TSecureShell::ResetConnection()
  62. {
  63. FreeBackend();
  64. ClearStdError();
  65. PendLen = 0;
  66. PendSize = 0;
  67. sfree(Pending);
  68. Pending = NULL;
  69. FCWriteTemp = "";
  70. ResetSessionInfo();
  71. FAuthenticating = false;
  72. FAuthenticated = false;
  73. FStoredPasswordTried = false;
  74. FStoredPasswordTriedForKI = false;
  75. }
  76. //---------------------------------------------------------------------------
  77. void __fastcall TSecureShell::ResetSessionInfo()
  78. {
  79. FSessionInfoValid = false;
  80. FMaxPacketSize = NULL;
  81. }
  82. //---------------------------------------------------------------------------
  83. inline void __fastcall TSecureShell::UpdateSessionInfo()
  84. {
  85. if (!FSessionInfoValid)
  86. {
  87. FSshVersion = get_ssh_version(FBackendHandle);
  88. FSessionInfo.ProtocolBaseName = "SSH";
  89. FSessionInfo.ProtocolName =
  90. FORMAT("%s-%d", (FSessionInfo.ProtocolBaseName, get_ssh_version(FBackendHandle)));
  91. FSessionInfo.SecurityProtocolName = FSessionInfo.ProtocolName;
  92. FSessionInfo.CSCompression =
  93. FuncToCompression(FSshVersion, get_cscomp(FBackendHandle));
  94. FSessionInfo.SCCompression =
  95. FuncToCompression(FSshVersion, get_sccomp(FBackendHandle));
  96. if (FSshVersion == 1)
  97. {
  98. FSessionInfo.CSCipher = CipherNames[FuncToSsh1Cipher(get_cipher(FBackendHandle))];
  99. FSessionInfo.SCCipher = CipherNames[FuncToSsh1Cipher(get_cipher(FBackendHandle))];
  100. }
  101. else
  102. {
  103. FSessionInfo.CSCipher = CipherNames[FuncToSsh2Cipher(get_cscipher(FBackendHandle))];
  104. FSessionInfo.SCCipher = CipherNames[FuncToSsh2Cipher(get_sccipher(FBackendHandle))];
  105. }
  106. FSessionInfoValid = true;
  107. }
  108. }
  109. //---------------------------------------------------------------------------
  110. const TSessionInfo & __fastcall TSecureShell::GetSessionInfo()
  111. {
  112. if (!FSessionInfoValid)
  113. {
  114. UpdateSessionInfo();
  115. }
  116. return FSessionInfo;
  117. }
  118. //---------------------------------------------------------------------
  119. void __fastcall TSecureShell::ClearConfig(Config * cfg)
  120. {
  121. StrDispose(cfg->remote_cmd_ptr);
  122. StrDispose(cfg->remote_cmd_ptr2);
  123. // clear all
  124. memset(cfg, 0, sizeof(*cfg));
  125. }
  126. //---------------------------------------------------------------------
  127. void __fastcall TSecureShell::StoreToConfig(TSessionData * Data, Config * cfg, bool Simple)
  128. {
  129. ClearConfig(cfg);
  130. // user-configurable settings
  131. ASCOPY(cfg->host, Data->HostName);
  132. ASCOPY(cfg->username, Data->UserName);
  133. cfg->port = Data->PortNumber;
  134. cfg->protocol = PROT_SSH;
  135. // always set 0, as we will handle keepalives ourselves to avoid
  136. // multi-threaded issues in putty timer list
  137. cfg->ping_interval = 0;
  138. cfg->compression = Data->Compression;
  139. cfg->tryagent = Data->TryAgent;
  140. cfg->agentfwd = Data->AgentFwd;
  141. cfg->addressfamily = Data->AddressFamily;
  142. ASCOPY(cfg->ssh_rekey_data, Data->RekeyData);
  143. cfg->ssh_rekey_time = Data->RekeyTime;
  144. for (int c = 0; c < CIPHER_COUNT; c++)
  145. {
  146. int pcipher;
  147. switch (Data->Cipher[c]) {
  148. case cipWarn: pcipher = CIPHER_WARN; break;
  149. case cip3DES: pcipher = CIPHER_3DES; break;
  150. case cipBlowfish: pcipher = CIPHER_BLOWFISH; break;
  151. case cipAES: pcipher = CIPHER_AES; break;
  152. case cipDES: pcipher = CIPHER_DES; break;
  153. case cipArcfour: pcipher = CIPHER_ARCFOUR; break;
  154. default: assert(false);
  155. }
  156. cfg->ssh_cipherlist[c] = pcipher;
  157. }
  158. for (int k = 0; k < KEX_COUNT; k++)
  159. {
  160. int pkex;
  161. switch (Data->Kex[k]) {
  162. case kexWarn: pkex = KEX_WARN; break;
  163. case kexDHGroup1: pkex = KEX_DHGROUP1; break;
  164. case kexDHGroup14: pkex = KEX_DHGROUP14; break;
  165. case kexDHGEx: pkex = KEX_DHGEX; break;
  166. case kexRSA: pkex = KEX_RSA; break;
  167. default: assert(false);
  168. }
  169. cfg->ssh_kexlist[k] = pkex;
  170. }
  171. AnsiString SPublicKeyFile = Data->PublicKeyFile;
  172. if (SPublicKeyFile.IsEmpty()) SPublicKeyFile = Configuration->DefaultKeyFile;
  173. SPublicKeyFile = StripPathQuotes(ExpandEnvironmentVariables(SPublicKeyFile));
  174. ASCOPY(cfg->keyfile.path, SPublicKeyFile);
  175. cfg->sshprot = Data->SshProt;
  176. cfg->ssh2_des_cbc = Data->Ssh2DES;
  177. cfg->ssh_no_userauth = Data->SshNoUserAuth;
  178. cfg->try_tis_auth = Data->AuthTIS;
  179. cfg->try_ki_auth = Data->AuthKI;
  180. cfg->try_gssapi_auth = Data->AuthGSSAPI;
  181. cfg->gssapifwd = Data->GSSAPIFwdTGT;
  182. cfg->change_username = Data->ChangeUsername;
  183. cfg->proxy_type = Data->ProxyMethod;
  184. ASCOPY(cfg->proxy_host, Data->ProxyHost);
  185. cfg->proxy_port = Data->ProxyPort;
  186. ASCOPY(cfg->proxy_username, Data->ProxyUsername);
  187. ASCOPY(cfg->proxy_password, Data->ProxyPassword);
  188. if (Data->ProxyMethod == pmCmd)
  189. {
  190. ASCOPY(cfg->proxy_telnet_command, Data->ProxyLocalCommand);
  191. }
  192. else
  193. {
  194. ASCOPY(cfg->proxy_telnet_command, Data->ProxyTelnetCommand);
  195. }
  196. cfg->proxy_dns = Data->ProxyDNS;
  197. cfg->even_proxy_localhost = Data->ProxyLocalhost;
  198. #pragma option push -w-eas
  199. // after 0.53b values were reversed, however putty still stores
  200. // settings to registry in save way as before
  201. cfg->sshbug_ignore1 = Data->Bug[sbIgnore1];
  202. cfg->sshbug_plainpw1 = Data->Bug[sbPlainPW1];
  203. cfg->sshbug_rsa1 = Data->Bug[sbRSA1];
  204. cfg->sshbug_hmac2 = Data->Bug[sbHMAC2];
  205. cfg->sshbug_derivekey2 = Data->Bug[sbDeriveKey2];
  206. cfg->sshbug_rsapad2 = Data->Bug[sbRSAPad2];
  207. cfg->sshbug_rekey2 = Data->Bug[sbRekey2];
  208. // new after 0.53b
  209. cfg->sshbug_pksessid2 = Data->Bug[sbPKSessID2];
  210. cfg->sshbug_maxpkt2 = Data->Bug[sbMaxPkt2];
  211. #pragma option pop
  212. if (!Data->TunnelPortFwd.IsEmpty())
  213. {
  214. assert(!Simple);
  215. ASCOPY(cfg->portfwd, Data->TunnelPortFwd);
  216. // when setting up a tunnel, do not open shell/sftp
  217. cfg->ssh_no_shell = TRUE;
  218. }
  219. else
  220. {
  221. assert(Simple);
  222. cfg->ssh_simple = Simple;
  223. if (Data->FSProtocol == fsSCPonly)
  224. {
  225. cfg->ssh_subsys = FALSE;
  226. if (Data->Shell.IsEmpty())
  227. {
  228. // Following forces Putty to open default shell
  229. // see ssh.c: do_ssh2_authconn() and ssh1_protocol()
  230. cfg->remote_cmd[0] = '\0';
  231. }
  232. else
  233. {
  234. cfg->remote_cmd_ptr = StrNew(Data->Shell.c_str());
  235. }
  236. }
  237. else
  238. {
  239. if (Data->SftpServer.IsEmpty())
  240. {
  241. cfg->ssh_subsys = TRUE;
  242. strcpy(cfg->remote_cmd, "sftp");
  243. }
  244. else
  245. {
  246. cfg->ssh_subsys = FALSE;
  247. cfg->remote_cmd_ptr = StrNew(Data->SftpServer.c_str());
  248. }
  249. if (Data->FSProtocol != fsSFTPonly)
  250. {
  251. cfg->ssh_subsys2 = FALSE;
  252. if (Data->Shell.IsEmpty())
  253. {
  254. // Following forces Putty to open default shell
  255. // see ssh.c: do_ssh2_authconn() and ssh1_protocol()
  256. cfg->remote_cmd_ptr2 = StrNew("\0");
  257. }
  258. else
  259. {
  260. cfg->remote_cmd_ptr2 = StrNew(Data->Shell.c_str());
  261. }
  262. }
  263. if ((Data->FSProtocol == fsSFTPonly) && Data->SftpServer.IsEmpty())
  264. {
  265. // see psftp_connect() from psftp.c
  266. cfg->ssh_subsys2 = FALSE;
  267. cfg->remote_cmd_ptr2 = StrNew(
  268. "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n"
  269. "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n"
  270. "exec sftp-server");
  271. }
  272. }
  273. }
  274. // permanent settings
  275. cfg->nopty = TRUE;
  276. cfg->tcp_keepalives = 0;
  277. cfg->ssh_show_banner = TRUE;
  278. for (int Index = 0; Index < ngsslibs; Index++)
  279. {
  280. cfg->ssh_gsslist[Index] = gsslibkeywords[Index].v;
  281. }
  282. }
  283. //---------------------------------------------------------------------------
  284. void __fastcall TSecureShell::Open()
  285. {
  286. FBackend = &ssh_backend;
  287. ResetConnection();
  288. FAuthenticating = false;
  289. FAuthenticated = false;
  290. Active = false;
  291. FAuthenticationLog = "";
  292. FUI->Information(LoadStr(STATUS_LOOKUPHOST), true);
  293. StoreToConfig(FSessionData, FConfig, Simple);
  294. char * RealHost;
  295. FreeBackend(); // in case we are reconnecting
  296. const char * InitError = FBackend->init(this, &FBackendHandle, FConfig,
  297. FSessionData->HostName.c_str(), FSessionData->PortNumber, &RealHost, 0,
  298. FConfig->tcp_keepalives);
  299. sfree(RealHost);
  300. if (InitError)
  301. {
  302. PuttyFatalError(InitError);
  303. }
  304. FUI->Information(LoadStr(STATUS_CONNECT), true);
  305. Init();
  306. CheckConnection(CONNECTION_FAILED);
  307. FLastDataSent = Now();
  308. FSessionInfo.LoginTime = Now();
  309. FAuthenticating = false;
  310. FAuthenticated = true;
  311. FUI->Information(LoadStr(STATUS_AUTHENTICATED), true);
  312. ResetSessionInfo();
  313. assert(!FSessionInfo.SshImplementation.IsEmpty());
  314. FOpened = true;
  315. }
  316. //---------------------------------------------------------------------------
  317. void __fastcall TSecureShell::Init()
  318. {
  319. try
  320. {
  321. try
  322. {
  323. // Recent pscp checks FBackend->exitcode(FBackendHandle) in the loop
  324. // (see comment in putty revision 8110)
  325. // It seems that we do not need to do it.
  326. while (!get_ssh_state_session(FBackendHandle))
  327. {
  328. if (Configuration->ActualLogProtocol >= 1)
  329. {
  330. LogEvent("Waiting for the server to continue with the initialisation");
  331. }
  332. WaitForData();
  333. }
  334. // unless this is tunnel session, it must be safe to send now
  335. assert(FBackend->sendok(FBackendHandle) || !FSessionData->TunnelPortFwd.IsEmpty());
  336. }
  337. catch(Exception & E)
  338. {
  339. if (FAuthenticating && !FAuthenticationLog.IsEmpty())
  340. {
  341. FUI->FatalError(&E, FMTLOAD(AUTHENTICATION_LOG, (FAuthenticationLog)));
  342. }
  343. else
  344. {
  345. throw;
  346. }
  347. }
  348. }
  349. catch(Exception & E)
  350. {
  351. if (FAuthenticating)
  352. {
  353. FUI->FatalError(&E, LoadStr(AUTHENTICATION_FAILED));
  354. }
  355. else
  356. {
  357. throw;
  358. }
  359. }
  360. }
  361. //---------------------------------------------------------------------------
  362. void __fastcall TSecureShell::PuttyLogEvent(const AnsiString & Str)
  363. {
  364. #define SERVER_VERSION_MSG "Server version: "
  365. // Gross hack
  366. if (Str.Pos(SERVER_VERSION_MSG) == 1)
  367. {
  368. FSessionInfo.SshVersionString = Str.SubString(strlen(SERVER_VERSION_MSG) + 1,
  369. Str.Length() - strlen(SERVER_VERSION_MSG));
  370. const char * Ptr = strchr(FSessionInfo.SshVersionString.c_str(), '-');
  371. if (Ptr != NULL)
  372. {
  373. Ptr = strchr(Ptr + 1, '-');
  374. }
  375. FSessionInfo.SshImplementation = (Ptr != NULL) ? Ptr + 1 : "";
  376. }
  377. #define FORWARDING_FAILURE_MSG "Forwarded connection refused by server: "
  378. else if (Str.Pos(FORWARDING_FAILURE_MSG) == 1)
  379. {
  380. FLastTunnelError = Str.SubString(strlen(FORWARDING_FAILURE_MSG) + 1,
  381. Str.Length() - strlen(FORWARDING_FAILURE_MSG));
  382. static const TPuttyTranslation Translation[] = {
  383. { "Administratively prohibited [%]", PFWD_TRANSL_ADMIN },
  384. { "Connect failed [%]", PFWD_TRANSL_CONNECT },
  385. };
  386. TranslatePuttyMessage(Translation, LENOF(Translation), FLastTunnelError);
  387. }
  388. LogEvent(Str);
  389. }
  390. //---------------------------------------------------------------------------
  391. bool __fastcall TSecureShell::PromptUser(bool /*ToServer*/,
  392. AnsiString AName, bool /*NameRequired*/,
  393. AnsiString Instructions, bool InstructionsRequired,
  394. TStrings * Prompts, TStrings * Results)
  395. {
  396. // there can be zero prompts!
  397. assert(Results->Count == Prompts->Count);
  398. TPromptKind PromptKind;
  399. // beware of changing order
  400. static const TPuttyTranslation NameTranslation[] = {
  401. { "SSH login name", USERNAME_TITLE },
  402. { "SSH key passphrase", PASSPHRASE_TITLE },
  403. { "SSH TIS authentication", SERVER_PROMPT_TITLE },
  404. { "SSH CryptoCard authentication", SERVER_PROMPT_TITLE },
  405. { "SSH server: %", SERVER_PROMPT_TITLE2 },
  406. { "SSH server authentication", SERVER_PROMPT_TITLE },
  407. { "SSH password", PASSWORD_TITLE },
  408. { "New SSH password", NEW_PASSWORD_TITLE },
  409. };
  410. AnsiString Name = AName;
  411. int Index = TranslatePuttyMessage(NameTranslation, LENOF(NameTranslation), Name);
  412. const TPuttyTranslation * InstructionTranslation = NULL;
  413. const TPuttyTranslation * PromptTranslation = NULL;
  414. size_t PromptTranslationCount = 1;
  415. if (Index == 0) // username
  416. {
  417. static const TPuttyTranslation UsernamePromptTranslation[] = {
  418. { "login as: ", USERNAME_PROMPT2 },
  419. };
  420. PromptTranslation = UsernamePromptTranslation;
  421. PromptKind = pkUserName;
  422. }
  423. else if (Index == 1) // passhrase
  424. {
  425. static const TPuttyTranslation PassphrasePromptTranslation[] = {
  426. { "Passphrase for key \"%\": ", PROMPT_KEY_PASSPHRASE },
  427. };
  428. PromptTranslation = PassphrasePromptTranslation;
  429. PromptKind = pkPassphrase;
  430. }
  431. else if (Index == 2) // TIS
  432. {
  433. static const TPuttyTranslation TISInstructionTranslation[] = {
  434. { "Using TIS authentication.%", TIS_INSTRUCTION },
  435. };
  436. static const TPuttyTranslation TISPromptTranslation[] = {
  437. { "Response: ", PROMPT_PROMPT },
  438. };
  439. InstructionTranslation = TISInstructionTranslation;
  440. PromptTranslation = TISPromptTranslation;
  441. PromptKind = pkTIS;
  442. }
  443. else if (Index == 3) // CryptoCard
  444. {
  445. static const TPuttyTranslation CryptoCardInstructionTranslation[] = {
  446. { "Using CryptoCard authentication.%", CRYPTOCARD_INSTRUCTION },
  447. };
  448. static const TPuttyTranslation CryptoCardPromptTranslation[] = {
  449. { "Response: ", PROMPT_PROMPT },
  450. };
  451. InstructionTranslation = CryptoCardInstructionTranslation;
  452. PromptTranslation = CryptoCardPromptTranslation;
  453. PromptKind = pkCryptoCard;
  454. }
  455. else if ((Index == 4) || (Index == 5))
  456. {
  457. static const TPuttyTranslation KeybInteractiveInstructionTranslation[] = {
  458. { "Using keyboard-interactive authentication.%", KEYBINTER_INSTRUCTION },
  459. };
  460. InstructionTranslation = KeybInteractiveInstructionTranslation;
  461. PromptKind = pkKeybInteractive;
  462. }
  463. else if (Index == 6)
  464. {
  465. assert(Prompts->Count == 1);
  466. Prompts->Strings[0] = LoadStr(PASSWORD_PROMPT);
  467. PromptKind = pkPassword;
  468. }
  469. else if (Index == 7)
  470. {
  471. static const TPuttyTranslation NewPasswordPromptTranslation[] = {
  472. { "Current password (blank for previously entered password): ", NEW_PASSWORD_CURRENT_PROMPT },
  473. { "Enter new password: ", NEW_PASSWORD_NEW_PROMPT },
  474. { "Confirm new password: ", NEW_PASSWORD_CONFIRM_PROMPT },
  475. };
  476. PromptTranslation = NewPasswordPromptTranslation;
  477. PromptTranslationCount = LENOF(NewPasswordPromptTranslation);
  478. PromptKind = pkNewPassword;
  479. }
  480. else
  481. {
  482. PromptKind = pkPrompt;
  483. assert(false);
  484. }
  485. LogEvent(FORMAT("Prompt (%d, %s, %s, %s)", (PromptKind, AName, Instructions, (Prompts->Count > 0 ? Prompts->Strings[0] : AnsiString("<no prompt>")))));
  486. Name = Name.Trim();
  487. if (InstructionTranslation != NULL)
  488. {
  489. TranslatePuttyMessage(InstructionTranslation, 1, Instructions);
  490. }
  491. // some servers add leading blank line to make the prompt look prettier
  492. // on terminal console
  493. Instructions = Instructions.Trim();
  494. for (int Index = 0; Index < Prompts->Count; Index++)
  495. {
  496. AnsiString Prompt = Prompts->Strings[Index];
  497. if (PromptTranslation != NULL)
  498. {
  499. TranslatePuttyMessage(PromptTranslation, PromptTranslationCount, Prompt);
  500. }
  501. // some servers add leading blank line to make the prompt look prettier
  502. // on terminal console
  503. Prompts->Strings[Index] = Prompt.Trim();
  504. }
  505. bool Result = false;
  506. if (PromptKind == pkUserName)
  507. {
  508. if (FSessionData->AuthGSSAPI)
  509. {
  510. // use empty username if no username was filled on login dialog
  511. // and GSSAPI auth is enabled, hence there's chance that the server can
  512. // deduce the username otherwise
  513. Results->Strings[0] = "";
  514. Result = true;
  515. }
  516. }
  517. else if ((PromptKind == pkTIS) || (PromptKind == pkCryptoCard) ||
  518. (PromptKind == pkKeybInteractive))
  519. {
  520. if (FSessionData->AuthKIPassword && !FSessionData->Password.IsEmpty() &&
  521. !FStoredPasswordTriedForKI && (Prompts->Count == 1) &&
  522. !bool(Prompts->Objects[0]))
  523. {
  524. LogEvent("Using stored password.");
  525. FUI->Information(LoadStr(AUTH_PASSWORD), false);
  526. Result = true;
  527. Results->Strings[0] = FSessionData->Password;
  528. FStoredPasswordTriedForKI = true;
  529. }
  530. else if (Instructions.IsEmpty() && !InstructionsRequired && (Prompts->Count == 0))
  531. {
  532. LogEvent("Ignoring empty SSH server authentication request");
  533. Result = true;
  534. }
  535. }
  536. else if (PromptKind == pkPassword)
  537. {
  538. if (!FSessionData->Password.IsEmpty() && !FStoredPasswordTried)
  539. {
  540. LogEvent("Using stored password.");
  541. FUI->Information(LoadStr(AUTH_PASSWORD), false);
  542. Result = true;
  543. Results->Strings[0] = FSessionData->Password;
  544. FStoredPasswordTried = true;
  545. }
  546. }
  547. if (!Result)
  548. {
  549. Result = FUI->PromptUser(FSessionData,
  550. PromptKind, Name, Instructions, Prompts, Results);
  551. if (Result)
  552. {
  553. if ((PromptKind == pkUserName) && (Prompts->Count == 1))
  554. {
  555. FUserName = Results->Strings[0];
  556. }
  557. }
  558. }
  559. return Result;
  560. }
  561. //---------------------------------------------------------------------------
  562. void __fastcall TSecureShell::GotHostKey()
  563. {
  564. // due to re-key GotHostKey() may be called again later during session
  565. if (!FAuthenticating && !FAuthenticated)
  566. {
  567. FAuthenticating = true;
  568. FUI->Information(LoadStr(STATUS_AUTHENTICATE), true);
  569. }
  570. }
  571. //---------------------------------------------------------------------------
  572. void __fastcall TSecureShell::CWrite(const char * Data, int Length)
  573. {
  574. // some messages to stderr may indicate that something has changed with the
  575. // session, so reset the session info
  576. ResetSessionInfo();
  577. // We send only whole line at once, so we have to cache incoming data
  578. FCWriteTemp += DeleteChar(AnsiString(Data, Length), '\r');
  579. AnsiString Line;
  580. // Do we have at least one complete line in std error cache?
  581. while (FCWriteTemp.Pos("\n") > 0)
  582. {
  583. AnsiString Line = CutToChar(FCWriteTemp, '\n', false);
  584. FLog->Add(llStdError, Line);
  585. if (FAuthenticating)
  586. {
  587. TranslateAuthenticationMessage(Line);
  588. FAuthenticationLog += (FAuthenticationLog.IsEmpty() ? "" : "\n") + Line;
  589. }
  590. FUI->Information(Line, false);
  591. }
  592. }
  593. //---------------------------------------------------------------------------
  594. void __fastcall TSecureShell::RegisterReceiveHandler(TNotifyEvent Handler)
  595. {
  596. assert(FOnReceive == NULL);
  597. FOnReceive = Handler;
  598. }
  599. //---------------------------------------------------------------------------
  600. void __fastcall TSecureShell::UnregisterReceiveHandler(TNotifyEvent Handler)
  601. {
  602. assert(FOnReceive == Handler);
  603. USEDPARAM(Handler);
  604. FOnReceive = NULL;
  605. }
  606. //---------------------------------------------------------------------------
  607. void __fastcall TSecureShell::FromBackend(bool IsStdErr, const char * Data, int Length)
  608. {
  609. CheckConnection();
  610. if (Configuration->ActualLogProtocol >= 1)
  611. {
  612. LogEvent(FORMAT("Received %u bytes (%d)", (Length, int(IsStdErr))));
  613. }
  614. // Following is taken from scp.c from_backend() and modified
  615. if (IsStdErr)
  616. {
  617. AddStdError(AnsiString(Data, Length));
  618. }
  619. else
  620. {
  621. unsigned char *p = (unsigned char *)Data;
  622. unsigned Len = (unsigned)Length;
  623. // with event-select mechanism we can now receive data even before we
  624. // actually expect them (OutPtr can be NULL)
  625. if ((OutPtr != NULL) && (OutLen > 0) && (Len > 0))
  626. {
  627. unsigned Used = OutLen;
  628. if (Used > Len) Used = Len;
  629. memcpy(OutPtr, p, Used);
  630. OutPtr += Used; OutLen -= Used;
  631. p += Used; Len -= Used;
  632. }
  633. if (Len > 0)
  634. {
  635. if (PendSize < PendLen + Len)
  636. {
  637. PendSize = PendLen + Len + 4096;
  638. Pending = (char *)
  639. (Pending ? srealloc(Pending, PendSize) : smalloc(PendSize));
  640. if (!Pending) FatalError("Out of memory");
  641. }
  642. memcpy(Pending + PendLen, p, Len);
  643. PendLen += Len;
  644. }
  645. if (FOnReceive != NULL)
  646. {
  647. if (!FFrozen)
  648. {
  649. FFrozen = true;
  650. try
  651. {
  652. do
  653. {
  654. FDataWhileFrozen = false;
  655. FOnReceive(NULL);
  656. }
  657. while (FDataWhileFrozen);
  658. }
  659. __finally
  660. {
  661. FFrozen = false;
  662. }
  663. }
  664. else
  665. {
  666. FDataWhileFrozen = true;
  667. }
  668. }
  669. }
  670. }
  671. //---------------------------------------------------------------------------
  672. bool __fastcall TSecureShell::Peek(char *& Buf, int Len)
  673. {
  674. bool Result = (int(PendLen) >= Len);
  675. if (Result)
  676. {
  677. Buf = Pending;
  678. }
  679. return Result;
  680. }
  681. //---------------------------------------------------------------------------
  682. Integer __fastcall TSecureShell::Receive(char * Buf, Integer Len)
  683. {
  684. CheckConnection();
  685. if (Len > 0)
  686. {
  687. // Following is taken from scp.c ssh_scp_recv() and modified
  688. OutPtr = Buf;
  689. OutLen = Len;
  690. try
  691. {
  692. /*
  693. * See if the pending-input block contains some of what we
  694. * need.
  695. */
  696. if (PendLen > 0)
  697. {
  698. unsigned PendUsed = PendLen;
  699. if (PendUsed > OutLen)
  700. {
  701. PendUsed = OutLen;
  702. }
  703. memcpy(OutPtr, Pending, PendUsed);
  704. memmove(Pending, Pending + PendUsed, PendLen - PendUsed);
  705. OutPtr += PendUsed;
  706. OutLen -= PendUsed;
  707. PendLen -= PendUsed;
  708. if (PendLen == 0)
  709. {
  710. PendSize = 0;
  711. sfree(Pending);
  712. Pending = NULL;
  713. }
  714. }
  715. while (OutLen > 0)
  716. {
  717. if (Configuration->ActualLogProtocol >= 1)
  718. {
  719. LogEvent(FORMAT("Waiting for another %u bytes", (static_cast<int>(OutLen))));
  720. }
  721. WaitForData();
  722. }
  723. // This seems ambiguous
  724. if (Len <= 0) FatalError(LoadStr(LOST_CONNECTION));
  725. }
  726. __finally
  727. {
  728. OutPtr = NULL;
  729. }
  730. };
  731. if (Configuration->ActualLogProtocol >= 1)
  732. {
  733. LogEvent(FORMAT("Read %u bytes (%d pending)",
  734. (static_cast<int>(Len), static_cast<int>(PendLen))));
  735. }
  736. return Len;
  737. }
  738. //---------------------------------------------------------------------------
  739. AnsiString __fastcall TSecureShell::ReceiveLine()
  740. {
  741. unsigned Index;
  742. Char Ch;
  743. AnsiString Line;
  744. Boolean EOL = False;
  745. do
  746. {
  747. // If there is any buffer of received chars
  748. if (PendLen > 0)
  749. {
  750. Index = 0;
  751. // Repeat until we walk thru whole buffer or reach end-of-line
  752. while ((Index < PendLen) && (!Index || (Pending[Index-1] != '\n')))
  753. {
  754. Index++;
  755. }
  756. EOL = (Boolean)(Index && (Pending[Index-1] == '\n'));
  757. Integer PrevLen = Line.Length();
  758. Line.SetLength(PrevLen + Index);
  759. Receive(Line.c_str() + PrevLen, Index);
  760. }
  761. // If buffer don't contain end-of-line character
  762. // we read one more which causes receiving new buffer of chars
  763. if (!EOL)
  764. {
  765. Receive(&Ch, 1);
  766. Line += Ch;
  767. EOL = (Ch == '\n');
  768. }
  769. }
  770. while (!EOL);
  771. // We don't want end-of-line character
  772. Line.SetLength(Line.Length()-1);
  773. CaptureOutput(llOutput, Line);
  774. return Line;
  775. }
  776. //---------------------------------------------------------------------------
  777. void __fastcall TSecureShell::SendSpecial(int Code)
  778. {
  779. LogEvent(FORMAT("Sending special code: %d", (Code)));
  780. CheckConnection();
  781. FBackend->special(FBackendHandle, (Telnet_Special)Code);
  782. CheckConnection();
  783. FLastDataSent = Now();
  784. }
  785. //---------------------------------------------------------------------------
  786. void __fastcall TSecureShell::SendEOF()
  787. {
  788. SendSpecial(TS_EOF);
  789. }
  790. //---------------------------------------------------------------------------
  791. int __fastcall TSecureShell::TimeoutPrompt(TQueryParamsTimerEvent PoolEvent)
  792. {
  793. FWaiting++;
  794. int Answer;
  795. try
  796. {
  797. TQueryParams Params(qpFatalAbort | qpAllowContinueOnError);
  798. Params.Timer = 500;
  799. Params.TimerEvent = PoolEvent;
  800. Params.TimerMessage = FMTLOAD(TIMEOUT_STILL_WAITING2, (FSessionData->Timeout));
  801. Params.TimerAnswers = qaAbort;
  802. Answer = FUI->QueryUser(FMTLOAD(CONFIRM_PROLONG_TIMEOUT3, (FSessionData->Timeout)),
  803. NULL, qaRetry | qaAbort, &Params);
  804. }
  805. __finally
  806. {
  807. FWaiting--;
  808. }
  809. return Answer;
  810. }
  811. //---------------------------------------------------------------------------
  812. void __fastcall TSecureShell::SendBuffer(unsigned int & Result)
  813. {
  814. // for comments see PoolForData
  815. if (!Active)
  816. {
  817. Result = qaRetry;
  818. }
  819. else
  820. {
  821. try
  822. {
  823. if (FBackend->sendbuffer(FBackendHandle) <= MAX_BUFSIZE)
  824. {
  825. Result = qaOK;
  826. }
  827. }
  828. catch(...)
  829. {
  830. Result = qaRetry;
  831. }
  832. }
  833. }
  834. //---------------------------------------------------------------------------
  835. void __fastcall TSecureShell::DispatchSendBuffer(int BufSize)
  836. {
  837. TDateTime Start = Now();
  838. do
  839. {
  840. CheckConnection();
  841. if (Configuration->ActualLogProtocol >= 1)
  842. {
  843. LogEvent(FORMAT("There are %u bytes remaining in the send buffer, "
  844. "need to send at least another %u bytes",
  845. (BufSize, BufSize - MAX_BUFSIZE)));
  846. }
  847. EventSelectLoop(100, false, NULL);
  848. BufSize = FBackend->sendbuffer(FBackendHandle);
  849. if (Configuration->ActualLogProtocol >= 1)
  850. {
  851. LogEvent(FORMAT("There are %u bytes remaining in the send buffer", (BufSize)));
  852. }
  853. if (Now() - Start > FSessionData->TimeoutDT)
  854. {
  855. LogEvent("Waiting for dispatching send buffer timed out, asking user what to do.");
  856. int Answer = TimeoutPrompt(SendBuffer);
  857. switch (Answer)
  858. {
  859. case qaRetry:
  860. Start = Now();
  861. break;
  862. case qaOK:
  863. BufSize = 0;
  864. break;
  865. default:
  866. assert(false);
  867. // fallthru
  868. case qaAbort:
  869. FatalError(LoadStr(USER_TERMINATED));
  870. break;
  871. }
  872. }
  873. }
  874. while (BufSize > MAX_BUFSIZE);
  875. }
  876. //---------------------------------------------------------------------------
  877. void __fastcall TSecureShell::Send(const char * Buf, Integer Len)
  878. {
  879. CheckConnection();
  880. int BufSize = FBackend->send(FBackendHandle, (char *)Buf, Len);
  881. if (Configuration->ActualLogProtocol >= 1)
  882. {
  883. LogEvent(FORMAT("Sent %u bytes", (static_cast<int>(Len))));
  884. LogEvent(FORMAT("There are %u bytes remaining in the send buffer", (BufSize)));
  885. }
  886. FLastDataSent = Now();
  887. // among other forces receive of pending data to free the servers's send buffer
  888. EventSelectLoop(0, false, NULL);
  889. if (BufSize > MAX_BUFSIZE)
  890. {
  891. DispatchSendBuffer(BufSize);
  892. }
  893. CheckConnection();
  894. }
  895. //---------------------------------------------------------------------------
  896. void __fastcall TSecureShell::SendNull()
  897. {
  898. LogEvent("Sending NULL.");
  899. Send("", 1);
  900. }
  901. //---------------------------------------------------------------------------
  902. void __fastcall TSecureShell::SendStr(AnsiString Str)
  903. {
  904. CheckConnection();
  905. Send(Str.c_str(), Str.Length());
  906. }
  907. //---------------------------------------------------------------------------
  908. void __fastcall TSecureShell::SendLine(AnsiString Line)
  909. {
  910. SendStr(Line);
  911. Send("\n", 1);
  912. FLog->Add(llInput, Line);
  913. }
  914. //---------------------------------------------------------------------------
  915. int __fastcall TSecureShell::TranslatePuttyMessage(
  916. const TPuttyTranslation * Translation, size_t Count, AnsiString & Message)
  917. {
  918. int Result = -1;
  919. for (unsigned int Index = 0; Index < Count; Index++)
  920. {
  921. const char * Original = Translation[Index].Original;
  922. const char * Div = strchr(Original, '%');
  923. if (Div == NULL)
  924. {
  925. if (strcmp(Message.c_str(), Original) == 0)
  926. {
  927. Message = LoadStr(Translation[Index].Translation);
  928. Result = int(Index);
  929. break;
  930. }
  931. }
  932. else
  933. {
  934. size_t OriginalLen = strlen(Original);
  935. size_t PrefixLen = Div - Original;
  936. size_t SuffixLen = OriginalLen - PrefixLen - 1;
  937. if (((size_t)Message.Length() >= OriginalLen - 1) &&
  938. (strncmp(Message.c_str(), Original, PrefixLen) == 0) &&
  939. (strncmp(Message.c_str() + Message.Length() - SuffixLen, Div + 1, SuffixLen) == 0))
  940. {
  941. Message = FMTLOAD(Translation[Index].Translation,
  942. (Message.SubString(PrefixLen + 1, Message.Length() - PrefixLen - SuffixLen).TrimRight()));
  943. Result = int(Index);
  944. break;
  945. }
  946. }
  947. }
  948. return Result;
  949. }
  950. //---------------------------------------------------------------------------
  951. int __fastcall TSecureShell::TranslateAuthenticationMessage(AnsiString & Message)
  952. {
  953. static const TPuttyTranslation Translation[] = {
  954. { "Using username \"%\".", AUTH_TRANSL_USERNAME },
  955. { "Using keyboard-interactive authentication.", AUTH_TRANSL_KEYB_INTER }, // not used anymore
  956. { "Authenticating with public key \"%\" from agent", AUTH_TRANSL_PUBLIC_KEY_AGENT },
  957. { "Authenticating with public key \"%\"", AUTH_TRANSL_PUBLIC_KEY },
  958. { "Authenticated using RSA key \"%\" from agent", AUTH_TRANSL_PUBLIC_KEY_AGENT },
  959. { "Wrong passphrase", AUTH_TRANSL_WRONG_PASSPHRASE },
  960. { "Wrong passphrase.", AUTH_TRANSL_WRONG_PASSPHRASE },
  961. { "Access denied", AUTH_TRANSL_ACCESS_DENIED },
  962. { "Trying public key authentication.", AUTH_TRANSL_TRY_PUBLIC_KEY },
  963. { "Server refused our public key.", AUTH_TRANSL_KEY_REFUSED },
  964. { "Server refused our key", AUTH_TRANSL_KEY_REFUSED }
  965. };
  966. return TranslatePuttyMessage(Translation, LENOF(Translation), Message);
  967. }
  968. //---------------------------------------------------------------------------
  969. void __fastcall TSecureShell::AddStdError(AnsiString Str)
  970. {
  971. FStdError += Str;
  972. Integer P;
  973. Str = DeleteChar(Str, '\r');
  974. // We send only whole line at once to log, so we have to cache
  975. // incoming std error data
  976. FStdErrorTemp += Str;
  977. AnsiString Line;
  978. // Do we have at least one complete line in std error cache?
  979. while ((P = FStdErrorTemp.Pos("\n")) > 0)
  980. {
  981. Line = FStdErrorTemp.SubString(1, P-1);
  982. FStdErrorTemp.Delete(1, P);
  983. AddStdErrorLine(Line);
  984. }
  985. }
  986. //---------------------------------------------------------------------------
  987. void __fastcall TSecureShell::AddStdErrorLine(const AnsiString & Str)
  988. {
  989. if (FAuthenticating)
  990. {
  991. FAuthenticationLog += (FAuthenticationLog.IsEmpty() ? "" : "\n") + Str;
  992. }
  993. CaptureOutput(llStdError, Str);
  994. }
  995. //---------------------------------------------------------------------------
  996. const AnsiString & __fastcall TSecureShell::GetStdError()
  997. {
  998. return FStdError;
  999. }
  1000. //---------------------------------------------------------------------------
  1001. void __fastcall TSecureShell::ClearStdError()
  1002. {
  1003. // Flush std error cache
  1004. if (!FStdErrorTemp.IsEmpty())
  1005. {
  1006. if (FAuthenticating)
  1007. {
  1008. FAuthenticationLog +=
  1009. (FAuthenticationLog.IsEmpty() ? "" : "\n") + FStdErrorTemp;
  1010. }
  1011. CaptureOutput(llStdError, FStdErrorTemp);
  1012. FStdErrorTemp = "";
  1013. }
  1014. FStdError = "";
  1015. }
  1016. //---------------------------------------------------------------------------
  1017. void __fastcall TSecureShell::CaptureOutput(TLogLineType Type,
  1018. const AnsiString & Line)
  1019. {
  1020. if (FOnCaptureOutput != NULL)
  1021. {
  1022. FOnCaptureOutput(Line, (Type == llStdError));
  1023. }
  1024. FLog->Add(Type, Line);
  1025. }
  1026. //---------------------------------------------------------------------------
  1027. int __fastcall TSecureShell::TranslateErrorMessage(AnsiString & Message)
  1028. {
  1029. static const TPuttyTranslation Translation[] = {
  1030. { "Server unexpectedly closed network connection", UNEXPECTED_CLOSE_ERROR },
  1031. { "Network error: Connection refused", NET_TRANSL_REFUSED },
  1032. { "Network error: Connection reset by peer", NET_TRANSL_RESET },
  1033. { "Network error: Connection timed out", NET_TRANSL_TIMEOUT },
  1034. };
  1035. return TranslatePuttyMessage(Translation, LENOF(Translation), Message);
  1036. }
  1037. //---------------------------------------------------------------------------
  1038. void __fastcall TSecureShell::PuttyFatalError(AnsiString Error)
  1039. {
  1040. TranslateErrorMessage(Error);
  1041. FatalError(Error);
  1042. }
  1043. //---------------------------------------------------------------------------
  1044. void __fastcall TSecureShell::FatalError(AnsiString Error)
  1045. {
  1046. FUI->FatalError(NULL, Error);
  1047. }
  1048. //---------------------------------------------------------------------------
  1049. void __fastcall inline TSecureShell::LogEvent(const AnsiString & Str)
  1050. {
  1051. if (FLog->Logging)
  1052. {
  1053. FLog->Add(llMessage, Str);
  1054. }
  1055. }
  1056. //---------------------------------------------------------------------------
  1057. void __fastcall TSecureShell::SocketEventSelect(SOCKET Socket, HANDLE Event, bool Startup)
  1058. {
  1059. int Events;
  1060. if (Startup)
  1061. {
  1062. Events = (FD_CONNECT | FD_READ | FD_WRITE | FD_OOB | FD_CLOSE | FD_ACCEPT);
  1063. }
  1064. else
  1065. {
  1066. Events = 0;
  1067. }
  1068. if (Configuration->ActualLogProtocol >= 2)
  1069. {
  1070. LogEvent(FORMAT("Selecting events %d for socket %d", (int(Events), int(Socket))));
  1071. }
  1072. if (WSAEventSelect(Socket, (WSAEVENT)Event, Events) == SOCKET_ERROR)
  1073. {
  1074. if (Configuration->ActualLogProtocol >= 2)
  1075. {
  1076. LogEvent(FORMAT("Error selecting events %d for socket %d", (int(Events), int(Socket))));
  1077. }
  1078. if (Startup)
  1079. {
  1080. FatalError(FMTLOAD(EVENT_SELECT_ERROR, (WSAGetLastError())));
  1081. }
  1082. }
  1083. }
  1084. //---------------------------------------------------------------------------
  1085. void __fastcall TSecureShell::UpdateSocket(SOCKET value, bool Startup)
  1086. {
  1087. if (!FActive && !Startup)
  1088. {
  1089. // no-op
  1090. // Remove the branch eventualy:
  1091. // When TCP connection fails, PuTTY does not release the memory allocate for
  1092. // socket. As a simple hack we call sk_tcp_close() in ssh.c to release the memory,
  1093. // until they fix it better. Unfortunately sk_tcp_close calls do_select,
  1094. // so we must filter that out.
  1095. }
  1096. else
  1097. {
  1098. assert(value);
  1099. assert((FActive && (FSocket == value)) || (!FActive && Startup));
  1100. // filter our "local proxy" connection, which have no socket
  1101. if (value != INVALID_SOCKET)
  1102. {
  1103. SocketEventSelect(value, FSocketEvent, Startup);
  1104. }
  1105. else
  1106. {
  1107. assert(FSessionData->ProxyMethod == pmCmd);
  1108. }
  1109. if (Startup)
  1110. {
  1111. FSocket = value;
  1112. FActive = true;
  1113. }
  1114. else
  1115. {
  1116. FSocket = INVALID_SOCKET;
  1117. Discard();
  1118. }
  1119. }
  1120. }
  1121. //---------------------------------------------------------------------------
  1122. void __fastcall TSecureShell::UpdatePortFwdSocket(SOCKET value, bool Startup)
  1123. {
  1124. if (Configuration->ActualLogProtocol >= 2)
  1125. {
  1126. LogEvent(FORMAT("Updating forwarding socket %d (%d)", (int(value), int(Startup))));
  1127. }
  1128. SocketEventSelect(value, FSocketEvent, Startup);
  1129. if (Startup)
  1130. {
  1131. FPortFwdSockets.insert(value);
  1132. }
  1133. else
  1134. {
  1135. FPortFwdSockets.erase(value);
  1136. }
  1137. }
  1138. //---------------------------------------------------------------------------
  1139. void __fastcall TSecureShell::SetActive(bool value)
  1140. {
  1141. if (FActive != value)
  1142. {
  1143. if (value)
  1144. {
  1145. Open();
  1146. }
  1147. else
  1148. {
  1149. Close();
  1150. }
  1151. }
  1152. }
  1153. //---------------------------------------------------------------------------
  1154. void __fastcall TSecureShell::FreeBackend()
  1155. {
  1156. if (FBackendHandle != NULL)
  1157. {
  1158. FBackend->free(FBackendHandle);
  1159. FBackendHandle = NULL;
  1160. }
  1161. }
  1162. //---------------------------------------------------------------------------
  1163. void __fastcall TSecureShell::Discard()
  1164. {
  1165. bool WasActive = FActive;
  1166. FActive = false;
  1167. FOpened = false;
  1168. if (WasActive)
  1169. {
  1170. FUI->Closed();
  1171. }
  1172. }
  1173. //---------------------------------------------------------------------------
  1174. void __fastcall TSecureShell::Close()
  1175. {
  1176. LogEvent("Closing connection.");
  1177. assert(FActive);
  1178. // this is particularly necessary when using local proxy command
  1179. // (e.g. plink), otherwise it hangs in sk_localproxy_close
  1180. SendEOF();
  1181. FreeBackend();
  1182. Discard();
  1183. }
  1184. //---------------------------------------------------------------------------
  1185. void inline __fastcall TSecureShell::CheckConnection(int Message)
  1186. {
  1187. if (!FActive || get_ssh_state_closed(FBackendHandle))
  1188. {
  1189. AnsiString Str = LoadStr(Message >= 0 ? Message : NOT_CONNECTED);
  1190. int ExitCode = get_ssh_exitcode(FBackendHandle);
  1191. if (ExitCode >= 0)
  1192. {
  1193. Str += " " + FMTLOAD(SSH_EXITCODE, (ExitCode));
  1194. }
  1195. FatalError(Str);
  1196. }
  1197. }
  1198. //---------------------------------------------------------------------------
  1199. void __fastcall TSecureShell::PoolForData(WSANETWORKEVENTS & Events, unsigned int & Result)
  1200. {
  1201. if (!Active)
  1202. {
  1203. // see comment below
  1204. Result = qaRetry;
  1205. }
  1206. else
  1207. {
  1208. try
  1209. {
  1210. if (Configuration->ActualLogProtocol >= 2)
  1211. {
  1212. LogEvent("Pooling for data in case they finally arrives");
  1213. }
  1214. // in extreme condition it may happen that send buffer is full, but there
  1215. // will be no data comming and we may not empty the send buffer because we
  1216. // do not process FD_WRITE until we receive any FD_READ
  1217. if (EventSelectLoop(0, false, &Events))
  1218. {
  1219. LogEvent("Data has arrived, closing query to user.");
  1220. Result = qaOK;
  1221. }
  1222. }
  1223. catch(...)
  1224. {
  1225. // if we let the exception out, it may popup another message dialog
  1226. // in whole event loop, another call to PoolForData from original dialog
  1227. // would be invoked, leading to an infinite loop.
  1228. // by retrying we hope (that probably fatal) error would repeat in WaitForData.
  1229. // anyway now once no actual work is done in EventSelectLoop,
  1230. // hardly any exception can occur actually
  1231. Result = qaRetry;
  1232. }
  1233. }
  1234. }
  1235. //---------------------------------------------------------------------------
  1236. class TPoolForDataEvent
  1237. {
  1238. public:
  1239. __fastcall TPoolForDataEvent(TSecureShell * SecureShell, WSANETWORKEVENTS & Events) :
  1240. FSecureShell(SecureShell),
  1241. FEvents(Events)
  1242. {
  1243. }
  1244. void __fastcall PoolForData(unsigned int & Result)
  1245. {
  1246. FSecureShell->PoolForData(FEvents, Result);
  1247. }
  1248. private:
  1249. TSecureShell * FSecureShell;
  1250. WSANETWORKEVENTS & FEvents;
  1251. };
  1252. //---------------------------------------------------------------------------
  1253. void __fastcall TSecureShell::WaitForData()
  1254. {
  1255. // see winsftp.c
  1256. bool IncomingData;
  1257. do
  1258. {
  1259. if (Configuration->ActualLogProtocol >= 2)
  1260. {
  1261. LogEvent("Looking for incoming data");
  1262. }
  1263. IncomingData = EventSelectLoop(FSessionData->Timeout * 1000, true, NULL);
  1264. if (!IncomingData)
  1265. {
  1266. WSANETWORKEVENTS Events;
  1267. memset(&Events, 0, sizeof(Events));
  1268. TPoolForDataEvent Event(this, Events);
  1269. LogEvent("Waiting for data timed out, asking user what to do.");
  1270. int Answer = TimeoutPrompt(Event.PoolForData);
  1271. switch (Answer)
  1272. {
  1273. case qaRetry:
  1274. // noop
  1275. break;
  1276. case qaOK:
  1277. // read event was already captured in PoolForData(),
  1278. // make sure we do not try to select it again as it would timeout
  1279. // unless another read event occurs
  1280. IncomingData = true;
  1281. HandleNetworkEvents(FSocket, Events);
  1282. break;
  1283. default:
  1284. assert(false);
  1285. // fallthru
  1286. case qaAbort:
  1287. FatalError(LoadStr(USER_TERMINATED));
  1288. break;
  1289. }
  1290. }
  1291. }
  1292. while (!IncomingData);
  1293. }
  1294. //---------------------------------------------------------------------------
  1295. bool __fastcall TSecureShell::SshFallbackCmd() const
  1296. {
  1297. return ssh_fallback_cmd(FBackendHandle);
  1298. }
  1299. //---------------------------------------------------------------------------
  1300. bool __fastcall TSecureShell::EnumNetworkEvents(SOCKET Socket, WSANETWORKEVENTS & Events)
  1301. {
  1302. if (Configuration->ActualLogProtocol >= 2)
  1303. {
  1304. LogEvent(FORMAT("Enumerating network events for socket %d", (int(Socket))));
  1305. }
  1306. // see winplink.c
  1307. WSANETWORKEVENTS AEvents;
  1308. if (WSAEnumNetworkEvents(Socket, NULL, &AEvents) == 0)
  1309. {
  1310. noise_ultralight(Socket);
  1311. noise_ultralight(AEvents.lNetworkEvents);
  1312. Events.lNetworkEvents |= AEvents.lNetworkEvents;
  1313. for (int Index = 0; Index < FD_MAX_EVENTS; Index++)
  1314. {
  1315. if (AEvents.iErrorCode[Index] != 0)
  1316. {
  1317. Events.iErrorCode[Index] = AEvents.iErrorCode[Index];
  1318. }
  1319. }
  1320. if (Configuration->ActualLogProtocol >= 2)
  1321. {
  1322. LogEvent(FORMAT("Enumerated %d network events making %d cumulative events for socket %d",
  1323. (int(AEvents.lNetworkEvents), int(Events.lNetworkEvents), int(Socket))));
  1324. }
  1325. }
  1326. else
  1327. {
  1328. if (Configuration->ActualLogProtocol >= 2)
  1329. {
  1330. LogEvent(FORMAT("Error enumerating network events for socket %d", (int(Socket))));
  1331. }
  1332. }
  1333. return
  1334. FLAGSET(Events.lNetworkEvents, FD_READ) ||
  1335. FLAGSET(Events.lNetworkEvents, FD_CLOSE);
  1336. }
  1337. //---------------------------------------------------------------------------
  1338. void __fastcall TSecureShell::HandleNetworkEvents(SOCKET Socket, WSANETWORKEVENTS & Events)
  1339. {
  1340. static const struct { int Bit, Mask; const char * Desc; } EventTypes[] =
  1341. {
  1342. { FD_READ_BIT, FD_READ, "read" },
  1343. { FD_WRITE_BIT, FD_WRITE, "write" },
  1344. { FD_OOB_BIT, FD_OOB, "oob" },
  1345. { FD_ACCEPT_BIT, FD_ACCEPT, "accept" },
  1346. { FD_CONNECT_BIT, FD_CONNECT, "connect" },
  1347. { FD_CLOSE_BIT, FD_CLOSE, "close" },
  1348. };
  1349. for (int Event = 0; Event < LENOF(EventTypes); Event++)
  1350. {
  1351. if (FLAGSET(Events.lNetworkEvents, EventTypes[Event].Mask))
  1352. {
  1353. int Err = Events.iErrorCode[EventTypes[Event].Bit];
  1354. if (Configuration->ActualLogProtocol >= 2)
  1355. {
  1356. LogEvent(FORMAT("Handling network %s event on socket %d with error %d",
  1357. (EventTypes[Event].Desc, int(Socket), Err)));
  1358. }
  1359. #pragma option push -w-prc
  1360. LPARAM SelectEvent = WSAMAKESELECTREPLY(EventTypes[Event].Mask, Err);
  1361. #pragma option pop
  1362. if (!select_result((WPARAM)Socket, SelectEvent))
  1363. {
  1364. // note that connection was closed definitely,
  1365. // so "check" is actually not required
  1366. CheckConnection();
  1367. }
  1368. }
  1369. }
  1370. }
  1371. //---------------------------------------------------------------------------
  1372. bool __fastcall TSecureShell::ProcessNetworkEvents(SOCKET Socket)
  1373. {
  1374. WSANETWORKEVENTS Events;
  1375. memset(&Events, 0, sizeof(Events));
  1376. bool Result = EnumNetworkEvents(Socket, Events);
  1377. HandleNetworkEvents(Socket, Events);
  1378. return Result;
  1379. }
  1380. //---------------------------------------------------------------------------
  1381. bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventRequired,
  1382. WSANETWORKEVENTS * Events)
  1383. {
  1384. CheckConnection();
  1385. bool Result = false;
  1386. do
  1387. {
  1388. if (Configuration->ActualLogProtocol >= 2)
  1389. {
  1390. LogEvent("Looking for network events");
  1391. }
  1392. unsigned int TicksBefore = GetTickCount();
  1393. int HandleCount;
  1394. // note that this returns all handles, not only the session-related handles
  1395. HANDLE * Handles = handle_get_events(&HandleCount);
  1396. try
  1397. {
  1398. Handles = sresize(Handles, HandleCount + 1, HANDLE);
  1399. Handles[HandleCount] = FSocketEvent;
  1400. unsigned int WaitResult = WaitForMultipleObjects(HandleCount + 1, Handles, FALSE, MSec);
  1401. if (WaitResult < WAIT_OBJECT_0 + HandleCount)
  1402. {
  1403. if (handle_got_event(Handles[WaitResult - WAIT_OBJECT_0]))
  1404. {
  1405. Result = true;
  1406. }
  1407. }
  1408. else if (WaitResult == WAIT_OBJECT_0 + HandleCount)
  1409. {
  1410. if (Configuration->ActualLogProtocol >= 1)
  1411. {
  1412. LogEvent("Detected network event");
  1413. }
  1414. if (Events == NULL)
  1415. {
  1416. if (ProcessNetworkEvents(FSocket))
  1417. {
  1418. Result = true;
  1419. }
  1420. }
  1421. else
  1422. {
  1423. if (EnumNetworkEvents(FSocket, *Events))
  1424. {
  1425. Result = true;
  1426. }
  1427. }
  1428. {
  1429. TSockets::iterator i = FPortFwdSockets.begin();
  1430. while (i != FPortFwdSockets.end())
  1431. {
  1432. ProcessNetworkEvents(*i);
  1433. i++;
  1434. }
  1435. }
  1436. }
  1437. else if (WaitResult == WAIT_TIMEOUT)
  1438. {
  1439. if (Configuration->ActualLogProtocol >= 2)
  1440. {
  1441. LogEvent("Timeout waiting for network events");
  1442. }
  1443. MSec = 0;
  1444. }
  1445. else
  1446. {
  1447. if (Configuration->ActualLogProtocol >= 2)
  1448. {
  1449. LogEvent(FORMAT("Unknown waiting result %d", (int(WaitResult))));
  1450. }
  1451. MSec = 0;
  1452. }
  1453. }
  1454. __finally
  1455. {
  1456. sfree(Handles);
  1457. }
  1458. unsigned int TicksAfter = GetTickCount();
  1459. // ticks wraps once in 49.7 days
  1460. if (TicksBefore < TicksAfter)
  1461. {
  1462. unsigned int Ticks = TicksAfter - TicksBefore;
  1463. if (Ticks > MSec)
  1464. {
  1465. MSec = 0;
  1466. }
  1467. else
  1468. {
  1469. MSec -= Ticks;
  1470. }
  1471. }
  1472. }
  1473. while (ReadEventRequired && (MSec > 0) && !Result);
  1474. return Result;
  1475. }
  1476. //---------------------------------------------------------------------------
  1477. void __fastcall TSecureShell::Idle(unsigned int MSec)
  1478. {
  1479. noise_regular();
  1480. call_ssh_timer(FBackendHandle);
  1481. EventSelectLoop(MSec, false, NULL);
  1482. }
  1483. //---------------------------------------------------------------------------
  1484. void __fastcall TSecureShell::KeepAlive()
  1485. {
  1486. if (FActive && (FWaiting == 0))
  1487. {
  1488. LogEvent("Sending null packet to keep session alive.");
  1489. SendSpecial(TS_PING);
  1490. }
  1491. else
  1492. {
  1493. // defer next keepalive attempt
  1494. FLastDataSent = Now();
  1495. }
  1496. }
  1497. //---------------------------------------------------------------------------
  1498. unsigned long __fastcall TSecureShell::MaxPacketSize()
  1499. {
  1500. if (!FSessionInfoValid)
  1501. {
  1502. UpdateSessionInfo();
  1503. }
  1504. if (FSshVersion == 1)
  1505. {
  1506. return 0;
  1507. }
  1508. else
  1509. {
  1510. if (FMaxPacketSize == NULL)
  1511. {
  1512. FMaxPacketSize = ssh2_remmaxpkt(FBackendHandle);
  1513. }
  1514. return *FMaxPacketSize;
  1515. }
  1516. }
  1517. //---------------------------------------------------------------------------
  1518. AnsiString __fastcall TSecureShell::FuncToCompression(
  1519. int SshVersion, const void * Compress) const
  1520. {
  1521. enum TCompressionType { ctNone, ctZLib };
  1522. if (SshVersion == 1)
  1523. {
  1524. return get_ssh1_compressing(FBackendHandle) ? "ZLib" : "";
  1525. }
  1526. else
  1527. {
  1528. return (ssh_compress *)Compress == &ssh_zlib ? "ZLib" : "";
  1529. }
  1530. }
  1531. //---------------------------------------------------------------------------
  1532. TCipher __fastcall TSecureShell::FuncToSsh1Cipher(const void * Cipher)
  1533. {
  1534. const ssh_cipher *CipherFuncs[] =
  1535. {&ssh_3des, &ssh_des, &ssh_blowfish_ssh1};
  1536. const TCipher TCiphers[] = {cip3DES, cipDES, cipBlowfish};
  1537. assert(LENOF(CipherFuncs) == LENOF(TCiphers));
  1538. TCipher Result = cipWarn;
  1539. for (int Index = 0; Index < LENOF(TCiphers); Index++)
  1540. {
  1541. if ((ssh_cipher *)Cipher == CipherFuncs[Index])
  1542. {
  1543. Result = TCiphers[Index];
  1544. }
  1545. }
  1546. assert(Result != cipWarn);
  1547. return Result;
  1548. }
  1549. //---------------------------------------------------------------------------
  1550. TCipher __fastcall TSecureShell::FuncToSsh2Cipher(const void * Cipher)
  1551. {
  1552. const ssh2_ciphers *CipherFuncs[] =
  1553. {&ssh2_3des, &ssh2_des, &ssh2_aes, &ssh2_blowfish, &ssh2_arcfour};
  1554. const TCipher TCiphers[] = {cip3DES, cipDES, cipAES, cipBlowfish, cipArcfour};
  1555. assert(LENOF(CipherFuncs) == LENOF(TCiphers));
  1556. TCipher Result = cipWarn;
  1557. for (int C = 0; C < LENOF(TCiphers); C++)
  1558. {
  1559. for (int F = 0; F < CipherFuncs[C]->nciphers; F++)
  1560. {
  1561. if ((ssh2_cipher *)Cipher == CipherFuncs[C]->list[F])
  1562. {
  1563. Result = TCiphers[C];
  1564. }
  1565. }
  1566. }
  1567. assert(Result != cipWarn);
  1568. return Result;
  1569. }
  1570. //---------------------------------------------------------------------------
  1571. struct TClipboardHandler
  1572. {
  1573. AnsiString Text;
  1574. void __fastcall Copy(TObject * /*Sender*/)
  1575. {
  1576. CopyToClipboard(Text);
  1577. }
  1578. };
  1579. //---------------------------------------------------------------------------
  1580. void __fastcall TSecureShell::VerifyHostKey(AnsiString Host, int Port,
  1581. const AnsiString KeyType, AnsiString KeyStr, const AnsiString Fingerprint)
  1582. {
  1583. GotHostKey();
  1584. char Delimiter = ';';
  1585. assert(KeyStr.Pos(Delimiter) == 0);
  1586. if (FSessionData->Tunnel)
  1587. {
  1588. Host = FSessionData->OrigHostName;
  1589. Port = FSessionData->OrigPortNumber;
  1590. }
  1591. FSessionInfo.HostKeyFingerprint = Fingerprint;
  1592. bool Result = false;
  1593. AnsiString Buf = FSessionData->HostKey;
  1594. while (!Result && !Buf.IsEmpty())
  1595. {
  1596. AnsiString ExpectedKey = CutToChar(Buf, Delimiter, false);
  1597. if (ExpectedKey == Fingerprint)
  1598. {
  1599. Result = true;
  1600. }
  1601. }
  1602. AnsiString StoredKeys;
  1603. if (!Result)
  1604. {
  1605. StoredKeys.SetLength(10240);
  1606. if (retrieve_host_key(Host.c_str(), Port, KeyType.c_str(),
  1607. StoredKeys.c_str(), StoredKeys.Length()) == 0)
  1608. {
  1609. PackStr(StoredKeys);
  1610. AnsiString Buf = StoredKeys;
  1611. while (!Result && !Buf.IsEmpty())
  1612. {
  1613. AnsiString StoredKey = CutToChar(Buf, Delimiter, false);
  1614. if (StoredKey == KeyStr)
  1615. {
  1616. Result = true;
  1617. }
  1618. }
  1619. }
  1620. else
  1621. {
  1622. StoredKeys = "";
  1623. }
  1624. }
  1625. if (!Result)
  1626. {
  1627. if (Configuration->DisableAcceptingHostKeys)
  1628. {
  1629. FatalError(LoadStr(KEY_NOT_VERIFIED));
  1630. }
  1631. else
  1632. {
  1633. TClipboardHandler ClipboardHandler;
  1634. ClipboardHandler.Text = Fingerprint;
  1635. bool Unknown = StoredKeys.IsEmpty();
  1636. int Answers;
  1637. int AliasesCount;
  1638. TQueryButtonAlias Aliases[3];
  1639. Aliases[0].Button = qaRetry;
  1640. Aliases[0].Alias = LoadStr(COPY_KEY_BUTTON);
  1641. Aliases[0].OnClick = &ClipboardHandler.Copy;
  1642. Answers = qaYes | qaCancel | qaRetry;
  1643. AliasesCount = 1;
  1644. if (!Unknown)
  1645. {
  1646. Aliases[1].Button = qaYes;
  1647. Aliases[1].Alias = LoadStr(UPDATE_KEY_BUTTON);
  1648. Aliases[2].Button = qaOK;
  1649. Aliases[2].Alias = LoadStr(ADD_KEY_BUTTON);
  1650. AliasesCount += 2;
  1651. Answers |= qaSkip | qaOK;
  1652. }
  1653. else
  1654. {
  1655. Answers |= qaNo;
  1656. }
  1657. TQueryParams Params;
  1658. Params.NoBatchAnswers = qaYes | qaRetry | qaSkip | qaOK;
  1659. Params.HelpKeyword = (Unknown ? HELP_UNKNOWN_KEY : HELP_DIFFERENT_KEY);
  1660. Params.Aliases = Aliases;
  1661. Params.AliasesCount = AliasesCount;
  1662. int R = FUI->QueryUser(
  1663. FMTLOAD((Unknown ? UNKNOWN_KEY2 : DIFFERENT_KEY3), (KeyType, Fingerprint)),
  1664. NULL, Answers, &Params, qtWarning);
  1665. switch (R) {
  1666. case qaOK:
  1667. assert(!Unknown);
  1668. KeyStr = (StoredKeys + Delimiter + KeyStr);
  1669. // fall thru
  1670. case qaYes:
  1671. store_host_key(Host.c_str(), Port, KeyType.c_str(), KeyStr.c_str());
  1672. break;
  1673. case qaCancel:
  1674. FatalError(LoadStr(KEY_NOT_VERIFIED));
  1675. }
  1676. }
  1677. }
  1678. }
  1679. //---------------------------------------------------------------------------
  1680. void __fastcall TSecureShell::AskAlg(const AnsiString AlgType,
  1681. const AnsiString AlgName)
  1682. {
  1683. AnsiString Msg;
  1684. if (AlgType == "key-exchange algorithm")
  1685. {
  1686. Msg = FMTLOAD(KEX_BELOW_TRESHOLD, (AlgName));
  1687. }
  1688. else
  1689. {
  1690. int CipherType;
  1691. if (AlgType == "cipher")
  1692. {
  1693. CipherType = CIPHER_TYPE_BOTH;
  1694. }
  1695. else if (AlgType == "client-to-server cipher")
  1696. {
  1697. CipherType = CIPHER_TYPE_CS;
  1698. }
  1699. else if (AlgType == "server-to-client cipher")
  1700. {
  1701. CipherType = CIPHER_TYPE_SC;
  1702. }
  1703. else
  1704. {
  1705. assert(false);
  1706. }
  1707. Msg = FMTLOAD(CIPHER_BELOW_TRESHOLD, (LoadStr(CipherType), AlgName));
  1708. }
  1709. if (FUI->QueryUser(Msg, NULL, qaYes | qaNo, NULL, qtWarning) == qaNo)
  1710. {
  1711. Abort();
  1712. }
  1713. }
  1714. //---------------------------------------------------------------------------
  1715. void __fastcall TSecureShell::DisplayBanner(const AnsiString & Banner)
  1716. {
  1717. FUI->DisplayBanner(Banner);
  1718. }
  1719. //---------------------------------------------------------------------------
  1720. void __fastcall TSecureShell::OldKeyfileWarning()
  1721. {
  1722. // actually never called, see Net.cpp
  1723. FUI->QueryUser(LoadStr(OLD_KEY), NULL, qaOK, NULL, qtWarning);
  1724. }
  1725. //---------------------------------------------------------------------------
  1726. bool __fastcall TSecureShell::GetStoredCredentialsTried()
  1727. {
  1728. return FStoredPasswordTried || FStoredPasswordTriedForKI;
  1729. }
  1730. //---------------------------------------------------------------------------
  1731. bool __fastcall TSecureShell::GetReady()
  1732. {
  1733. return FOpened && (FWaiting == 0);
  1734. }