SecureShell.cpp 59 KB

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