SecureShell.cpp 53 KB

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