SecureShell.cpp 53 KB

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