SecureShell.cpp 61 KB

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