PuttyIntf.cpp 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #define PUTTY_DO_GLOBALS
  5. #include "PuttyIntf.h"
  6. #include "Interface.h"
  7. #include "SecureShell.h"
  8. #include "Exceptions.h"
  9. #include "CoreMain.h"
  10. #include "TextsCore.h"
  11. #include <StrUtils.hpp>
  12. #include <Soap.EncdDecd.hpp>
  13. //---------------------------------------------------------------------------
  14. char sshver[50];
  15. extern const char commitid[] = "";
  16. const bool platform_uses_x11_unix_by_default = true;
  17. CRITICAL_SECTION putty_section;
  18. bool SaveRandomSeed;
  19. bool HadRandomSeed;
  20. char appname_[50];
  21. const char *const appname = appname_;
  22. extern const bool share_can_be_downstream = false;
  23. extern const bool share_can_be_upstream = false;
  24. THierarchicalStorage * PuttyStorage = NULL;
  25. //---------------------------------------------------------------------------
  26. extern "C"
  27. {
  28. #include <winstuff.h>
  29. }
  30. const UnicodeString OriginalPuttyRegistryStorageKey(_T(PUTTY_REG_POS));
  31. const UnicodeString KittyRegistryStorageKey(L"Software\\9bis.com\\KiTTY");
  32. const UnicodeString OriginalPuttyExecutable("putty.exe");
  33. const UnicodeString KittyExecutable("kitty.exe");
  34. const UnicodeString PuttyKeyExt(L"ppk");
  35. //---------------------------------------------------------------------------
  36. void __fastcall PuttyInitialize()
  37. {
  38. SaveRandomSeed = true;
  39. InitializeCriticalSection(&putty_section);
  40. HadRandomSeed = FileExists(ApiPath(Configuration->RandomSeedFileName));
  41. // make sure random generator is initialised, so random_save_seed()
  42. // in destructor can proceed
  43. random_ref();
  44. flags = FLAG_VERBOSE | FLAG_SYNCAGENT; // verbose log
  45. sk_init();
  46. AnsiString VersionString = AnsiString(SshVersionString());
  47. DebugAssert(!VersionString.IsEmpty() && (static_cast<size_t>(VersionString.Length()) < LENOF(sshver)));
  48. strcpy(sshver, VersionString.c_str());
  49. AnsiString AppName = AnsiString(AppNameString());
  50. DebugAssert(!AppName.IsEmpty() && (static_cast<size_t>(AppName.Length()) < LENOF(appname_)));
  51. strcpy(appname_, AppName.c_str());
  52. }
  53. //---------------------------------------------------------------------------
  54. static bool DeleteRandomSeedOnExit()
  55. {
  56. return !HadRandomSeed && !SaveRandomSeed;
  57. }
  58. //---------------------------------------------------------------------------
  59. void __fastcall PuttyFinalize()
  60. {
  61. if (SaveRandomSeed)
  62. {
  63. random_save_seed();
  64. }
  65. random_unref();
  66. // random_ref in PuttyInitialize creates the seed file. Delete it, if we didn't want to create it.
  67. if (DeleteRandomSeedOnExit())
  68. {
  69. DeleteFile(ApiPath(Configuration->RandomSeedFileName));
  70. }
  71. sk_cleanup();
  72. win_misc_cleanup();
  73. win_secur_cleanup();
  74. ec_cleanup();
  75. DeleteCriticalSection(&putty_section);
  76. }
  77. //---------------------------------------------------------------------------
  78. void __fastcall DontSaveRandomSeed()
  79. {
  80. SaveRandomSeed = false;
  81. }
  82. //---------------------------------------------------------------------------
  83. bool RandomSeedExists()
  84. {
  85. return
  86. !DeleteRandomSeedOnExit() &&
  87. FileExists(ApiPath(Configuration->RandomSeedFileName));
  88. }
  89. //---------------------------------------------------------------------------
  90. TSecureShell * GetSecureShell(Plug * plug, bool & pfwd)
  91. {
  92. if (!is_ssh(plug) && !is_pfwd(plug))
  93. {
  94. // If it is not SSH/PFwd plug, then it must be Proxy plug.
  95. // Get SSH/PFwd plug which it wraps.
  96. ProxySocket * AProxySocket = get_proxy_plug_socket(plug);
  97. plug = AProxySocket->plug;
  98. }
  99. pfwd = is_pfwd(plug);
  100. Seat * seat;
  101. if (pfwd)
  102. {
  103. seat = get_pfwd_seat(plug);
  104. }
  105. else
  106. {
  107. seat = get_ssh_seat(plug);
  108. }
  109. DebugAssert(seat != NULL);
  110. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  111. return SecureShell;
  112. }
  113. //---------------------------------------------------------------------------
  114. struct callback_set * get_callback_set(Plug * plug)
  115. {
  116. bool pfwd;
  117. TSecureShell * SecureShell = GetSecureShell(plug, pfwd);
  118. return SecureShell->GetCallbackSet();
  119. }
  120. //---------------------------------------------------------------------------
  121. struct callback_set * get_seat_callback_set(Seat * seat)
  122. {
  123. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  124. return SecureShell->GetCallbackSet();
  125. }
  126. //---------------------------------------------------------------------------
  127. extern "C" char * do_select(Plug * plug, SOCKET skt, bool startup)
  128. {
  129. bool pfwd;
  130. TSecureShell * SecureShell = GetSecureShell(plug, pfwd);
  131. if (!pfwd)
  132. {
  133. SecureShell->UpdateSocket(skt, startup);
  134. }
  135. else
  136. {
  137. SecureShell->UpdatePortFwdSocket(skt, startup);
  138. }
  139. return NULL;
  140. }
  141. //---------------------------------------------------------------------------
  142. static size_t output(Seat * seat, bool is_stderr, const void * data, size_t len)
  143. {
  144. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  145. if (static_cast<int>(static_cast<char>(is_stderr)) == -1)
  146. {
  147. SecureShell->CWrite(reinterpret_cast<const char *>(data), len);
  148. }
  149. else if (!is_stderr)
  150. {
  151. SecureShell->FromBackend(reinterpret_cast<const unsigned char *>(data), len);
  152. }
  153. else
  154. {
  155. SecureShell->AddStdError(reinterpret_cast<const char *>(data), len);
  156. }
  157. return 0;
  158. }
  159. //---------------------------------------------------------------------------
  160. static bool eof(Seat *)
  161. {
  162. return false;
  163. }
  164. //---------------------------------------------------------------------------
  165. static int get_userpass_input(Seat * seat, prompts_t * p, bufchain * DebugUsedArg(input))
  166. {
  167. DebugAssert(p != NULL);
  168. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  169. DebugAssert(SecureShell != NULL);
  170. int Result;
  171. TStrings * Prompts = new TStringList();
  172. TStrings * Results = new TStringList();
  173. try
  174. {
  175. UnicodeString Name = UTF8ToString(p->name);
  176. UnicodeString AName = Name;
  177. TPromptKind PromptKind = SecureShell->IdentifyPromptKind(AName);
  178. bool UTF8Prompt = (PromptKind != pkPassphrase);
  179. for (int Index = 0; Index < int(p->n_prompts); Index++)
  180. {
  181. prompt_t * Prompt = p->prompts[Index];
  182. UnicodeString S;
  183. if (UTF8Prompt)
  184. {
  185. S = UTF8ToString(Prompt->prompt);
  186. }
  187. else
  188. {
  189. S = UnicodeString(AnsiString(Prompt->prompt));
  190. }
  191. Prompts->AddObject(S, (TObject *)(FLAGMASK(Prompt->echo, pupEcho)));
  192. // this fails, when new passwords do not match on change password prompt,
  193. // and putty retries the prompt
  194. DebugAssert(Prompt->resultsize == 0);
  195. Results->Add(L"");
  196. }
  197. UnicodeString Instructions = UTF8ToString(p->instruction);
  198. if (SecureShell->PromptUser(p->to_server, Name, p->name_reqd,
  199. Instructions, p->instr_reqd, Prompts, Results))
  200. {
  201. for (int Index = 0; Index < int(p->n_prompts); Index++)
  202. {
  203. prompt_t * Prompt = p->prompts[Index];
  204. RawByteString S;
  205. if (UTF8Prompt)
  206. {
  207. S = RawByteString(UTF8String(Results->Strings[Index]));
  208. }
  209. else
  210. {
  211. S = RawByteString(AnsiString(Results->Strings[Index]));
  212. }
  213. prompt_set_result(Prompt, S.c_str());
  214. }
  215. Result = 1;
  216. }
  217. else
  218. {
  219. Result = 0;
  220. }
  221. }
  222. __finally
  223. {
  224. delete Prompts;
  225. delete Results;
  226. }
  227. return Result;
  228. }
  229. //---------------------------------------------------------------------------
  230. static void connection_fatal(Seat * seat, const char * message)
  231. {
  232. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  233. SecureShell->PuttyFatalError(UnicodeString(AnsiString(message)));
  234. }
  235. //---------------------------------------------------------------------------
  236. int verify_ssh_host_key(Seat * seat, const char * host, int port, const char * keytype,
  237. char * keystr, char * fingerprint, void (*/*callback*/)(void * ctx, int result),
  238. void * /*ctx*/)
  239. {
  240. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  241. SecureShell->VerifyHostKey(host, port, keytype, keystr, fingerprint);
  242. // We should return 0 when key was not confirmed, we throw exception instead.
  243. return 1;
  244. }
  245. //---------------------------------------------------------------------------
  246. bool have_ssh_host_key(Seat * seat, const char * hostname, int port,
  247. const char * keytype)
  248. {
  249. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  250. return SecureShell->HaveHostKey(hostname, port, keytype) ? 1 : 0;
  251. }
  252. //---------------------------------------------------------------------------
  253. int confirm_weak_crypto_primitive(Seat * seat, const char * algtype, const char * algname,
  254. void (*/*callback*/)(void * ctx, int result), void * /*ctx*/)
  255. {
  256. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  257. SecureShell->AskAlg(algtype, algname);
  258. // We should return 0 when alg was not confirmed, we throw exception instead.
  259. return 1;
  260. }
  261. //---------------------------------------------------------------------------
  262. int confirm_weak_cached_hostkey(Seat *, const char * /*algname*/, const char * /*betteralgs*/,
  263. void (*/*callback*/)(void *ctx, int result), void * /*ctx*/)
  264. {
  265. return 1;
  266. }
  267. //---------------------------------------------------------------------------
  268. void old_keyfile_warning(void)
  269. {
  270. // no reference to TSecureShell instance available
  271. }
  272. //---------------------------------------------------------------------------
  273. void display_banner(Seat * seat, const char * banner, int size)
  274. {
  275. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  276. UnicodeString Banner(UTF8String(banner, size));
  277. SecureShell->DisplayBanner(Banner);
  278. }
  279. //---------------------------------------------------------------------------
  280. static void SSHFatalError(const char * Format, va_list Param)
  281. {
  282. char Buf[200];
  283. vsnprintf(Buf, LENOF(Buf), Format, Param);
  284. Buf[LENOF(Buf) - 1] = '\0';
  285. // Only few calls from putty\winnet.c might be connected with specific
  286. // TSecureShell. Otherwise called only for really fatal errors
  287. // like 'out of memory' from putty\ssh.c.
  288. throw ESshFatal(NULL, Buf);
  289. }
  290. //---------------------------------------------------------------------------
  291. void modalfatalbox(const char * fmt, ...)
  292. {
  293. va_list Param;
  294. va_start(Param, fmt);
  295. SSHFatalError(fmt, Param);
  296. va_end(Param);
  297. }
  298. //---------------------------------------------------------------------------
  299. void nonfatal(const char * fmt, ...)
  300. {
  301. va_list Param;
  302. va_start(Param, fmt);
  303. SSHFatalError(fmt, Param);
  304. va_end(Param);
  305. }
  306. //---------------------------------------------------------------------------
  307. void ldisc_echoedit_update(Ldisc * /*handle*/)
  308. {
  309. DebugFail();
  310. }
  311. //---------------------------------------------------------------------------
  312. unsigned long schedule_timer(int ticks, timer_fn_t /*fn*/, void * /*ctx*/)
  313. {
  314. return ticks + GetTickCount();
  315. }
  316. //---------------------------------------------------------------------------
  317. void expire_timer_context(void * /*ctx*/)
  318. {
  319. // nothing
  320. }
  321. //---------------------------------------------------------------------------
  322. Pinger * pinger_new(Conf * /*conf*/, Backend * /*back*/)
  323. {
  324. return NULL;
  325. }
  326. //---------------------------------------------------------------------------
  327. void pinger_reconfig(Pinger * /*pinger*/, Conf * /*oldconf*/, Conf * /*newconf*/)
  328. {
  329. // nothing
  330. }
  331. //---------------------------------------------------------------------------
  332. void pinger_free(Pinger * /*pinger*/)
  333. {
  334. // nothing
  335. }
  336. //---------------------------------------------------------------------------
  337. void platform_get_x11_auth(struct X11Display * /*display*/, Conf * /*conf*/)
  338. {
  339. // nothing, therefore no auth.
  340. }
  341. //---------------------------------------------------------------------------
  342. // Based on PuTTY's settings.c
  343. char * get_remote_username(Conf * conf)
  344. {
  345. char * username = conf_get_str(conf, CONF_username);
  346. char * result;
  347. if (*username)
  348. {
  349. result = dupstr(username);
  350. }
  351. else
  352. {
  353. result = NULL;
  354. }
  355. return result;
  356. }
  357. //---------------------------------------------------------------------------
  358. static const SeatVtable ScpSeatVtable =
  359. {
  360. output,
  361. eof,
  362. get_userpass_input,
  363. nullseat_notify_remote_exit,
  364. connection_fatal,
  365. nullseat_update_specials_menu,
  366. nullseat_get_ttymode,
  367. nullseat_set_busy_status,
  368. verify_ssh_host_key,
  369. confirm_weak_crypto_primitive,
  370. confirm_weak_cached_hostkey,
  371. nullseat_is_always_utf8,
  372. nullseat_echoedit_update,
  373. nullseat_get_x_display,
  374. nullseat_get_windowid,
  375. nullseat_get_window_pixel_size,
  376. nullseat_stripctrl_new,
  377. nullseat_set_trust_status_vacuously
  378. };
  379. //---------------------------------------------------------------------------
  380. ScpSeat::ScpSeat(TSecureShell * ASecureShell)
  381. {
  382. SecureShell = ASecureShell;
  383. vt = &ScpSeatVtable;
  384. }
  385. //---------------------------------------------------------------------------
  386. static std::unique_ptr<TCriticalSection> PuttyRegistrySection(TraceInitPtr(new TCriticalSection()));
  387. enum TPuttyRegistryMode { prmPass, prmRedirect, prmCollect, prmFail };
  388. static TPuttyRegistryMode PuttyRegistryMode = prmRedirect;
  389. typedef std::map<UnicodeString, unsigned long> TPuttyRegistryTypes;
  390. TPuttyRegistryTypes PuttyRegistryTypes;
  391. //---------------------------------------------------------------------------
  392. static long OpenWinSCPKey(HKEY Key, const char * SubKey, HKEY * Result, bool CanCreate)
  393. {
  394. long R;
  395. DebugAssert(Key == HKEY_CURRENT_USER);
  396. DebugUsedParam(Key);
  397. UnicodeString RegKey = SubKey;
  398. int PuttyKeyLen = OriginalPuttyRegistryStorageKey.Length();
  399. DebugAssert(RegKey.SubString(1, PuttyKeyLen) == OriginalPuttyRegistryStorageKey);
  400. RegKey = RegKey.SubString(PuttyKeyLen + 1, RegKey.Length() - PuttyKeyLen);
  401. if (!RegKey.IsEmpty())
  402. {
  403. DebugAssert(RegKey[1] == L'\\');
  404. RegKey.Delete(1, 1);
  405. }
  406. if (RegKey.IsEmpty())
  407. {
  408. *Result = static_cast<HKEY>(NULL);
  409. R = ERROR_SUCCESS;
  410. }
  411. else
  412. {
  413. // we expect this to be called only from retrieve_host_key() or store_host_key()
  414. DebugAssert(RegKey == L"SshHostKeys");
  415. DebugAssert(PuttyStorage != NULL);
  416. DebugAssert(PuttyStorage->AccessMode == (CanCreate ? smReadWrite : smRead));
  417. if (PuttyStorage->OpenSubKey(RegKey, CanCreate))
  418. {
  419. *Result = reinterpret_cast<HKEY>(PuttyStorage);
  420. R = ERROR_SUCCESS;
  421. }
  422. else
  423. {
  424. R = ERROR_CANTOPEN;
  425. }
  426. }
  427. return R;
  428. }
  429. //---------------------------------------------------------------------------
  430. long reg_open_winscp_key(HKEY Key, const char * SubKey, HKEY * Result)
  431. {
  432. if (PuttyRegistryMode == prmPass)
  433. {
  434. return RegOpenKeyA(Key, SubKey, Result);
  435. }
  436. else if (PuttyRegistryMode == prmCollect)
  437. {
  438. *Result = reinterpret_cast<HKEY>(1);
  439. return ERROR_SUCCESS;
  440. }
  441. else if (PuttyRegistryMode == prmFail)
  442. {
  443. return ERROR_CANTOPEN;
  444. }
  445. DebugAssert(PuttyRegistryMode == prmRedirect);
  446. return OpenWinSCPKey(Key, SubKey, Result, false);
  447. }
  448. //---------------------------------------------------------------------------
  449. long reg_create_winscp_key(HKEY Key, const char * SubKey, HKEY * Result)
  450. {
  451. if (PuttyRegistryMode == prmPass)
  452. {
  453. return RegCreateKeyA(Key, SubKey, Result);
  454. }
  455. else if (PuttyRegistryMode == prmCollect)
  456. {
  457. *Result = reinterpret_cast<HKEY>(1);
  458. return ERROR_SUCCESS;
  459. }
  460. DebugAssert(PuttyRegistryMode == prmRedirect);
  461. return OpenWinSCPKey(Key, SubKey, Result, true);
  462. }
  463. //---------------------------------------------------------------------------
  464. long reg_query_winscp_value_ex(HKEY Key, const char * ValueName, unsigned long * Reserved,
  465. unsigned long * Type, unsigned char * Data, unsigned long * DataSize)
  466. {
  467. if (PuttyRegistryMode == prmPass)
  468. {
  469. return RegQueryValueExA(Key, ValueName, Reserved, Type, Data, DataSize);
  470. }
  471. else if (PuttyRegistryMode == prmCollect)
  472. {
  473. return ERROR_READ_FAULT;
  474. }
  475. DebugAssert(PuttyRegistryMode == prmRedirect);
  476. long R;
  477. DebugAssert(Configuration != NULL);
  478. THierarchicalStorage * Storage = reinterpret_cast<THierarchicalStorage *>(Key);
  479. AnsiString Value;
  480. if (Storage == NULL)
  481. {
  482. if (UnicodeString(ValueName) == L"RandSeedFile")
  483. {
  484. Value = AnsiString(Configuration->RandomSeedFileName);
  485. R = ERROR_SUCCESS;
  486. }
  487. else
  488. {
  489. DebugFail();
  490. R = ERROR_READ_FAULT;
  491. }
  492. }
  493. else
  494. {
  495. if (Storage->ValueExists(ValueName))
  496. {
  497. Value = AnsiString(Storage->ReadStringRaw(ValueName, L""));
  498. R = ERROR_SUCCESS;
  499. }
  500. else
  501. {
  502. R = ERROR_READ_FAULT;
  503. }
  504. }
  505. if (R == ERROR_SUCCESS)
  506. {
  507. DebugAssert(Type != NULL);
  508. *Type = REG_SZ;
  509. char * DataStr = reinterpret_cast<char *>(Data);
  510. strncpy(DataStr, Value.c_str(), *DataSize);
  511. DataStr[*DataSize - 1] = '\0';
  512. *DataSize = strlen(DataStr);
  513. }
  514. return R;
  515. }
  516. //---------------------------------------------------------------------------
  517. long reg_set_winscp_value_ex(HKEY Key, const char * ValueName, unsigned long Reserved,
  518. unsigned long Type, const unsigned char * Data, unsigned long DataSize)
  519. {
  520. if (PuttyRegistryMode == prmPass)
  521. {
  522. return RegSetValueExA(Key, ValueName, Reserved, Type, Data, DataSize);
  523. }
  524. else if (PuttyRegistryMode == prmCollect)
  525. {
  526. PuttyRegistryTypes[ValueName] = Type;
  527. return ERROR_SUCCESS;
  528. }
  529. DebugAssert(PuttyRegistryMode == prmRedirect);
  530. DebugAssert(Type == REG_SZ);
  531. DebugUsedParam(Type);
  532. THierarchicalStorage * Storage = reinterpret_cast<THierarchicalStorage *>(Key);
  533. DebugAssert(Storage != NULL);
  534. if (Storage != NULL)
  535. {
  536. UnicodeString Value(reinterpret_cast<const char*>(Data), DataSize - 1);
  537. Storage->WriteStringRaw(ValueName, Value);
  538. }
  539. return ERROR_SUCCESS;
  540. }
  541. //---------------------------------------------------------------------------
  542. long reg_close_winscp_key(HKEY Key)
  543. {
  544. if (PuttyRegistryMode == prmPass)
  545. {
  546. return RegCloseKey(Key);
  547. }
  548. else if (PuttyRegistryMode == prmCollect)
  549. {
  550. return ERROR_SUCCESS;
  551. }
  552. DebugAssert(PuttyRegistryMode == prmRedirect);
  553. return ERROR_SUCCESS;
  554. }
  555. //---------------------------------------------------------------------------
  556. TKeyType KeyType(UnicodeString FileName)
  557. {
  558. DebugAssert(ktUnopenable == SSH_KEYTYPE_UNOPENABLE);
  559. DebugAssert(ktSSHCom == SSH_KEYTYPE_SSHCOM);
  560. DebugAssert(ktSSH2PublicOpenSSH == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH);
  561. UTF8String UtfFileName = UTF8String(FileName);
  562. Filename * KeyFile = filename_from_str(UtfFileName.c_str());
  563. TKeyType Result = (TKeyType)key_type(KeyFile);
  564. filename_free(KeyFile);
  565. return Result;
  566. }
  567. //---------------------------------------------------------------------------
  568. bool IsKeyEncrypted(TKeyType KeyType, const UnicodeString & FileName, UnicodeString & Comment)
  569. {
  570. UTF8String UtfFileName = UTF8String(FileName);
  571. bool Result;
  572. char * CommentStr = NULL;
  573. Filename * KeyFile = filename_from_str(UtfFileName.c_str());
  574. try
  575. {
  576. switch (KeyType)
  577. {
  578. case ktSSH2:
  579. Result = (ssh2_userkey_encrypted(KeyFile, &CommentStr) != 0);
  580. break;
  581. case ktOpenSSHPEM:
  582. case ktOpenSSHNew:
  583. case ktSSHCom:
  584. Result = (import_encrypted(KeyFile, KeyType, &CommentStr) != NULL);
  585. break;
  586. default:
  587. DebugFail();
  588. Result = false;
  589. break;
  590. }
  591. }
  592. __finally
  593. {
  594. filename_free(KeyFile);
  595. }
  596. if (CommentStr != NULL)
  597. {
  598. Comment = UnicodeString(AnsiString(CommentStr));
  599. // ktOpenSSH has no comment, PuTTY defaults to file path
  600. if (Comment == FileName)
  601. {
  602. Comment = ExtractFileName(FileName);
  603. }
  604. sfree(CommentStr);
  605. }
  606. return Result;
  607. }
  608. //---------------------------------------------------------------------------
  609. TPrivateKey * LoadKey(TKeyType KeyType, const UnicodeString & FileName, const UnicodeString & Passphrase)
  610. {
  611. UTF8String UtfFileName = UTF8String(FileName);
  612. Filename * KeyFile = filename_from_str(UtfFileName.c_str());
  613. struct ssh2_userkey * Ssh2Key = NULL;
  614. const char * ErrorStr = NULL;
  615. AnsiString AnsiPassphrase = Passphrase;
  616. try
  617. {
  618. switch (KeyType)
  619. {
  620. case ktSSH2:
  621. Ssh2Key = ssh2_load_userkey(KeyFile, AnsiPassphrase.c_str(), &ErrorStr);
  622. break;
  623. case ktOpenSSHPEM:
  624. case ktOpenSSHNew:
  625. case ktSSHCom:
  626. Ssh2Key = import_ssh2(KeyFile, KeyType, AnsiPassphrase.c_str(), &ErrorStr);
  627. break;
  628. default:
  629. DebugFail();
  630. break;
  631. }
  632. }
  633. __finally
  634. {
  635. Shred(AnsiPassphrase);
  636. filename_free(KeyFile);
  637. }
  638. if (Ssh2Key == NULL)
  639. {
  640. UnicodeString Error = AnsiString(ErrorStr);
  641. // While theoretically we may get "unable to open key file" and
  642. // so we should check system error code,
  643. // we actully never get here unless we call KeyType previously
  644. // and handle ktUnopenable accordingly.
  645. throw Exception(Error);
  646. }
  647. else if (Ssh2Key == SSH2_WRONG_PASSPHRASE)
  648. {
  649. throw Exception(LoadStr(AUTH_TRANSL_WRONG_PASSPHRASE));
  650. }
  651. return reinterpret_cast<TPrivateKey *>(Ssh2Key);
  652. }
  653. //---------------------------------------------------------------------------
  654. void ChangeKeyComment(TPrivateKey * PrivateKey, const UnicodeString & Comment)
  655. {
  656. AnsiString AnsiComment(Comment);
  657. struct ssh2_userkey * Ssh2Key = reinterpret_cast<struct ssh2_userkey *>(PrivateKey);
  658. sfree(Ssh2Key->comment);
  659. Ssh2Key->comment = dupstr(AnsiComment.c_str());
  660. }
  661. //---------------------------------------------------------------------------
  662. void SaveKey(TKeyType KeyType, const UnicodeString & FileName,
  663. const UnicodeString & Passphrase, TPrivateKey * PrivateKey)
  664. {
  665. UTF8String UtfFileName = UTF8String(FileName);
  666. Filename * KeyFile = filename_from_str(UtfFileName.c_str());
  667. try
  668. {
  669. struct ssh2_userkey * Ssh2Key = reinterpret_cast<struct ssh2_userkey *>(PrivateKey);
  670. AnsiString AnsiPassphrase = Passphrase;
  671. char * PassphrasePtr = (AnsiPassphrase.IsEmpty() ? NULL : AnsiPassphrase.c_str());
  672. switch (KeyType)
  673. {
  674. case ktSSH2:
  675. if (!ssh2_save_userkey(KeyFile, Ssh2Key, PassphrasePtr))
  676. {
  677. int Error = errno;
  678. throw EOSExtException(FMTLOAD(KEY_SAVE_ERROR, (FileName)), Error);
  679. }
  680. break;
  681. default:
  682. DebugFail();
  683. break;
  684. }
  685. }
  686. __finally
  687. {
  688. filename_free(KeyFile);
  689. }
  690. }
  691. //---------------------------------------------------------------------------
  692. void FreeKey(TPrivateKey * PrivateKey)
  693. {
  694. struct ssh2_userkey * Ssh2Key = reinterpret_cast<struct ssh2_userkey *>(PrivateKey);
  695. ssh_key_free(Ssh2Key->key);
  696. sfree(Ssh2Key);
  697. }
  698. //---------------------------------------------------------------------------
  699. RawByteString LoadPublicKey(const UnicodeString & FileName, UnicodeString & Algorithm, UnicodeString & Comment)
  700. {
  701. RawByteString Result;
  702. UTF8String UtfFileName = UTF8String(FileName);
  703. Filename * KeyFile = filename_from_str(UtfFileName.c_str());
  704. try
  705. {
  706. char * AlgorithmStr = NULL;
  707. char * CommentStr = NULL;
  708. const char * ErrorStr = NULL;
  709. strbuf * PublicKeyBuf = strbuf_new();
  710. if (!ssh2_userkey_loadpub(KeyFile, &AlgorithmStr, BinarySink_UPCAST(PublicKeyBuf), &CommentStr, &ErrorStr))
  711. {
  712. UnicodeString Error = UnicodeString(AnsiString(ErrorStr));
  713. throw Exception(Error);
  714. }
  715. Algorithm = UnicodeString(AnsiString(AlgorithmStr));
  716. sfree(AlgorithmStr);
  717. Comment = UnicodeString(AnsiString(CommentStr));
  718. sfree(CommentStr);
  719. Result = RawByteString(reinterpret_cast<char *>(PublicKeyBuf->s), PublicKeyBuf->len);
  720. strbuf_free(PublicKeyBuf);
  721. }
  722. __finally
  723. {
  724. filename_free(KeyFile);
  725. }
  726. return Result;
  727. }
  728. //---------------------------------------------------------------------------
  729. UnicodeString GetPublicKeyLine(const UnicodeString & FileName, UnicodeString & Comment)
  730. {
  731. UnicodeString Algorithm;
  732. RawByteString PublicKey = LoadPublicKey(FileName, Algorithm, Comment);
  733. UnicodeString PublicKeyBase64 = EncodeBase64(PublicKey.c_str(), PublicKey.Length());
  734. PublicKeyBase64 = ReplaceStr(PublicKeyBase64, L"\r", L"");
  735. PublicKeyBase64 = ReplaceStr(PublicKeyBase64, L"\n", L"");
  736. UnicodeString Result = FORMAT(L"%s %s %s", (Algorithm, PublicKeyBase64, Comment));
  737. return Result;
  738. }
  739. //---------------------------------------------------------------------------
  740. bool __fastcall HasGSSAPI(UnicodeString CustomPath)
  741. {
  742. static int has = -1;
  743. if (has < 0)
  744. {
  745. Conf * conf = conf_new();
  746. ssh_gss_liblist * List = NULL;
  747. try
  748. {
  749. Filename * filename = filename_from_str(UTF8String(CustomPath).c_str());
  750. conf_set_filename(conf, CONF_ssh_gss_custom, filename);
  751. filename_free(filename);
  752. List = ssh_gss_setup(conf, NULL);
  753. for (int Index = 0; (has <= 0) && (Index < List->nlibraries); Index++)
  754. {
  755. ssh_gss_library * library = &List->libraries[Index];
  756. Ssh_gss_ctx ctx;
  757. memset(&ctx, 0, sizeof(ctx));
  758. has =
  759. ((library->acquire_cred(library, &ctx, NULL) == SSH_GSS_OK) &&
  760. (library->release_cred(library, &ctx) == SSH_GSS_OK)) ? 1 : 0;
  761. }
  762. }
  763. __finally
  764. {
  765. ssh_gss_cleanup(List);
  766. conf_free(conf);
  767. }
  768. if (has < 0)
  769. {
  770. has = 0;
  771. }
  772. }
  773. return (has > 0);
  774. }
  775. //---------------------------------------------------------------------------
  776. static void __fastcall DoNormalizeFingerprint(UnicodeString & Fingerprint, UnicodeString & KeyName, UnicodeString & KeyType)
  777. {
  778. const int MaxCount = 10;
  779. const ssh_keyalg * SignKeys[MaxCount];
  780. int Count = LENOF(SignKeys);
  781. // We may use find_pubkey_alg, but it gets complicated with normalized fingerprint
  782. // as the names have different number of dashes
  783. get_hostkey_algs(&Count, SignKeys);
  784. for (int Index = 0; Index < Count; Index++)
  785. {
  786. const ssh_keyalg * SignKey = SignKeys[Index];
  787. UnicodeString Name = UnicodeString(SignKey->ssh_id);
  788. if (StartsStr(Name + L" ", Fingerprint))
  789. {
  790. UnicodeString Rest = Fingerprint.SubString(Name.Length() + 2, Fingerprint.Length() - Name.Length() - 1);
  791. int Space = Rest.Pos(L" ");
  792. // If not a number, it's an invalid input,
  793. // either something completelly wrong, or it can be OpenSSH base64 public key,
  794. // that got here from TPasteKeyHandler::Paste
  795. if (IsNumber(Rest.SubString(1, Space - 1)))
  796. {
  797. KeyName = Name;
  798. Fingerprint = Rest.SubString(Space + 1, Fingerprint.Length() - Space);
  799. Fingerprint = Base64ToUrlSafe(Fingerprint);
  800. Fingerprint = MD5ToUrlSafe(Fingerprint);
  801. KeyType = UnicodeString(SignKey->cache_id);
  802. return;
  803. }
  804. }
  805. else if (StartsStr(Name + NormalizedFingerprintSeparator, Fingerprint))
  806. {
  807. KeyType = UnicodeString(SignKey->cache_id);
  808. KeyName = Name;
  809. Fingerprint.Delete(1, Name.Length() + 1);
  810. return;
  811. }
  812. }
  813. }
  814. //---------------------------------------------------------------------------
  815. void __fastcall NormalizeFingerprint(UnicodeString & Fingerprint, UnicodeString & KeyName)
  816. {
  817. UnicodeString KeyType;
  818. DoNormalizeFingerprint(Fingerprint, KeyName, KeyType);
  819. }
  820. //---------------------------------------------------------------------------
  821. UnicodeString __fastcall KeyTypeFromFingerprint(UnicodeString Fingerprint)
  822. {
  823. UnicodeString KeyType;
  824. UnicodeString KeyName; // unused
  825. DoNormalizeFingerprint(Fingerprint, KeyName, KeyType);
  826. return KeyType;
  827. }
  828. //---------------------------------------------------------------------------
  829. UnicodeString __fastcall GetPuTTYVersion()
  830. {
  831. // "Release 0.64"
  832. // "Pre-release 0.65:2015-07-20.95501a1"
  833. // "Development snapshot 2015-12-22.51465fa"
  834. UnicodeString Result = get_putty_version();
  835. // Skip "Release", "Pre-release", "Development snapshot"
  836. int P = Result.LastDelimiter(L" ");
  837. Result.Delete(1, P);
  838. return Result;
  839. }
  840. //---------------------------------------------------------------------------
  841. UnicodeString __fastcall Sha256(const char * Data, size_t Size)
  842. {
  843. unsigned char Digest[32];
  844. hash_simple(&ssh_sha256, make_ptrlen(Data, Size), Digest);
  845. UnicodeString Result(BytesToHex(Digest, LENOF(Digest)));
  846. return Result;
  847. }
  848. //---------------------------------------------------------------------------
  849. void __fastcall DllHijackingProtection()
  850. {
  851. dll_hijacking_protection();
  852. }
  853. //---------------------------------------------------------------------------
  854. UnicodeString __fastcall ParseOpenSshPubLine(const UnicodeString & Line, const struct ssh_keyalg *& Algorithm)
  855. {
  856. UTF8String UtfLine = UTF8String(Line);
  857. char * AlgorithmName = NULL;
  858. char * CommentPtr = NULL;
  859. const char * ErrorStr = NULL;
  860. strbuf * PubBlobBuf = strbuf_new();
  861. UnicodeString Result;
  862. if (!openssh_loadpub_line(UtfLine.c_str(), &AlgorithmName, BinarySink_UPCAST(PubBlobBuf), &CommentPtr, &ErrorStr))
  863. {
  864. throw Exception(UnicodeString(ErrorStr));
  865. }
  866. else
  867. {
  868. try
  869. {
  870. Algorithm = find_pubkey_alg(AlgorithmName);
  871. if (Algorithm == NULL)
  872. {
  873. throw Exception(FORMAT(L"Unknown public key algorithm \"%s\".", (AlgorithmName)));
  874. }
  875. ptrlen PtrLen = { PubBlobBuf->s, PubBlobBuf->len };
  876. ssh_key * Key = Algorithm->new_pub(Algorithm, PtrLen);
  877. if (Key == NULL)
  878. {
  879. throw Exception(L"Invalid public key.");
  880. }
  881. char * FmtKey = Algorithm->cache_str(Key);
  882. Result = UnicodeString(FmtKey);
  883. sfree(FmtKey);
  884. Algorithm->freekey(Key);
  885. }
  886. __finally
  887. {
  888. strbuf_free(PubBlobBuf);
  889. sfree(AlgorithmName);
  890. sfree(CommentPtr);
  891. }
  892. }
  893. return Result;
  894. }
  895. //---------------------------------------------------------------------------
  896. UnicodeString __fastcall GetSsh1KeyType()
  897. {
  898. return UnicodeString(ssh_rsa.cache_id);
  899. }
  900. //---------------------------------------------------------------------------
  901. UnicodeString __fastcall GetKeyTypeHuman(const UnicodeString & KeyType)
  902. {
  903. UnicodeString Result;
  904. if (KeyType == ssh_dss.cache_id)
  905. {
  906. Result = L"DSA";
  907. }
  908. else if ((KeyType == ssh_rsa.cache_id) ||
  909. (KeyType == L"rsa")) // SSH1
  910. {
  911. Result = L"RSA";
  912. }
  913. else if (KeyType == ssh_ecdsa_ed25519.cache_id)
  914. {
  915. Result = L"Ed25519";
  916. }
  917. else if (KeyType == ssh_ecdsa_nistp256.cache_id)
  918. {
  919. Result = L"ECDSA/nistp256";
  920. }
  921. else if (KeyType == ssh_ecdsa_nistp384.cache_id)
  922. {
  923. Result = L"ECDSA/nistp384";
  924. }
  925. else if (KeyType == ssh_ecdsa_nistp521.cache_id)
  926. {
  927. Result = L"ECDSA/nistp521";
  928. }
  929. else
  930. {
  931. DebugFail();
  932. Result = KeyType;
  933. }
  934. return Result;
  935. }
  936. //---------------------------------------------------------------------------
  937. bool IsOpenSSH(const UnicodeString & SshImplementation)
  938. {
  939. return
  940. // e.g. "OpenSSH_5.3"
  941. (SshImplementation.Pos(L"OpenSSH") == 1) ||
  942. // Sun SSH is based on OpenSSH (suffers the same bugs)
  943. (SshImplementation.Pos(L"Sun_SSH") == 1);
  944. }
  945. //---------------------------------------------------------------------------
  946. TStrings * SshCipherList()
  947. {
  948. std::unique_ptr<TStrings> Result(new TStringList());
  949. // Same order as DefaultCipherList
  950. const ssh2_ciphers * Ciphers[] = { &ssh2_aes, &ssh2_ccp, &ssh2_blowfish, &ssh2_3des, &ssh2_arcfour, &ssh2_des };
  951. for (unsigned int Index = 0; Index < LENOF(Ciphers); Index++)
  952. {
  953. for (int Index2 = 0; Index2 < Ciphers[Index]->nciphers; Index2++)
  954. {
  955. UnicodeString Name = UnicodeString(Ciphers[Index]->list[Index2]->ssh2_id);
  956. Result->Add(Name);
  957. }
  958. }
  959. return Result.release();
  960. }
  961. //---------------------------------------------------------------------------
  962. TStrings * SshKexList()
  963. {
  964. std::unique_ptr<TStrings> Result(new TStringList());
  965. // Same order as DefaultKexList
  966. const ssh_kexes * Kexes[] = { &ssh_ecdh_kex, &ssh_diffiehellman_gex, &ssh_diffiehellman_group14, &ssh_rsa_kex, &ssh_diffiehellman_group1 };
  967. for (unsigned int Index = 0; Index < LENOF(Kexes); Index++)
  968. {
  969. for (int Index2 = 0; Index2 < Kexes[Index]->nkexes; Index2++)
  970. {
  971. UnicodeString Name = UnicodeString(Kexes[Index]->list[Index2]->name);
  972. Result->Add(Name);
  973. }
  974. }
  975. return Result.release();
  976. }
  977. //---------------------------------------------------------------------------
  978. TStrings * SshHostKeyList()
  979. {
  980. std::unique_ptr<TStrings> Result(new TStringList());
  981. const int MaxCount = 10;
  982. const ssh_keyalg * SignKeys[MaxCount];
  983. int Count = LENOF(SignKeys);
  984. get_hostkey_algs(&Count, SignKeys);
  985. for (int Index = 0; Index < Count; Index++)
  986. {
  987. const ssh_keyalg * SignKey = SignKeys[Index];
  988. UnicodeString Name = UnicodeString(SignKey->ssh_id);
  989. Result->Add(Name);
  990. }
  991. return Result.release();
  992. }
  993. //---------------------------------------------------------------------------
  994. TStrings * SshMacList()
  995. {
  996. std::unique_ptr<TStrings> Result(new TStringList());
  997. const struct ssh2_macalg ** Macs = NULL;
  998. int Count = 0;
  999. get_macs(&Count, &Macs);
  1000. for (int Index = 0; Index < Count; Index++)
  1001. {
  1002. UnicodeString Name = UnicodeString(Macs[Index]->name);
  1003. Result->Add(Name);
  1004. }
  1005. return Result.release();
  1006. }
  1007. //---------------------------------------------------------------------------
  1008. UnicodeString GetCipherName(const ssh_cipher * Cipher)
  1009. {
  1010. return UnicodeString(UTF8String(Cipher->vt->text_name));
  1011. }
  1012. //---------------------------------------------------------------------------
  1013. UnicodeString GetCompressorName(const ssh_compressor * Compressor)
  1014. {
  1015. UnicodeString Result;
  1016. if (Compressor != NULL)
  1017. {
  1018. Result = UnicodeString(UTF8String(Compressor->vt->name));
  1019. }
  1020. return Result;
  1021. }
  1022. //---------------------------------------------------------------------------
  1023. UnicodeString GetDecompressorName(const ssh_decompressor * Decompressor)
  1024. {
  1025. UnicodeString Result;
  1026. if (Decompressor != NULL)
  1027. {
  1028. Result = UnicodeString(UTF8String(Decompressor->vt->name));
  1029. }
  1030. return Result;
  1031. }
  1032. //---------------------------------------------------------------------------
  1033. void WritePuttySettings(THierarchicalStorage * Storage, const UnicodeString & ASettings)
  1034. {
  1035. if (PuttyRegistryTypes.empty())
  1036. {
  1037. TGuard Guard(PuttyRegistrySection.get());
  1038. TValueRestorer<TPuttyRegistryMode> PuttyRegistryModeRestorer(PuttyRegistryMode);
  1039. PuttyRegistryMode = prmCollect;
  1040. Conf * conf = conf_new();
  1041. try
  1042. {
  1043. do_defaults(NULL, conf);
  1044. save_settings(NULL, conf);
  1045. }
  1046. __finally
  1047. {
  1048. conf_free(conf);
  1049. }
  1050. }
  1051. std::unique_ptr<TStrings> Settings(new TStringList());
  1052. UnicodeString Buf = ASettings;
  1053. UnicodeString Setting;
  1054. while (CutToken(Buf, Setting))
  1055. {
  1056. Settings->Add(Setting);
  1057. }
  1058. for (int Index = 0; Index < Settings->Count; Index++)
  1059. {
  1060. UnicodeString Name = Settings->Names[Index];
  1061. TPuttyRegistryTypes::const_iterator IType = PuttyRegistryTypes.find(Name);
  1062. if (IType != PuttyRegistryTypes.end())
  1063. {
  1064. UnicodeString Value = Settings->ValueFromIndex[Index];
  1065. int I;
  1066. if (IType->second == REG_SZ)
  1067. {
  1068. Storage->WriteStringRaw(Name, Value);
  1069. }
  1070. else if (DebugAlwaysTrue(IType->second == REG_DWORD) &&
  1071. TryStrToInt(Value, I))
  1072. {
  1073. Storage->WriteInteger(Name, I);
  1074. }
  1075. }
  1076. }
  1077. }
  1078. //---------------------------------------------------------------------------
  1079. void PuttyDefaults(Conf * conf)
  1080. {
  1081. TGuard Guard(PuttyRegistrySection.get());
  1082. TValueRestorer<TPuttyRegistryMode> PuttyRegistryModeRestorer(PuttyRegistryMode);
  1083. PuttyRegistryMode = prmFail;
  1084. do_defaults(NULL, conf);
  1085. }
  1086. //---------------------------------------------------------------------------
  1087. void SavePuttyDefaults(const UnicodeString & Name)
  1088. {
  1089. TGuard Guard(PuttyRegistrySection.get());
  1090. TValueRestorer<TPuttyRegistryMode> PuttyRegistryModeRestorer(PuttyRegistryMode);
  1091. PuttyRegistryMode = prmPass;
  1092. Conf * conf = conf_new();
  1093. try
  1094. {
  1095. PuttyDefaults(conf);
  1096. AnsiString PuttyName = PuttyStr(Name);
  1097. save_settings(PuttyName.c_str(), conf);
  1098. }
  1099. __finally
  1100. {
  1101. conf_free(conf);
  1102. }
  1103. }
  1104. //---------------------------------------------------------------------------
  1105. //---------------------------------------------------------------------------