PuttyIntf.cpp 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163
  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. //---------------------------------------------------------------------------
  25. extern "C"
  26. {
  27. #include <winstuff.h>
  28. }
  29. const UnicodeString OriginalPuttyRegistryStorageKey(_T(PUTTY_REG_POS));
  30. const UnicodeString KittyRegistryStorageKey(L"Software\\9bis.com\\KiTTY");
  31. const UnicodeString OriginalPuttyExecutable("putty.exe");
  32. const UnicodeString KittyExecutable("kitty.exe");
  33. const UnicodeString PuttyKeyExt(L"ppk");
  34. //---------------------------------------------------------------------------
  35. void __fastcall PuttyInitialize()
  36. {
  37. SaveRandomSeed = true;
  38. InitializeCriticalSection(&putty_section);
  39. HadRandomSeed = FileExists(ApiPath(Configuration->RandomSeedFileName));
  40. // make sure random generator is initialised, so random_save_seed()
  41. // in destructor can proceed
  42. random_ref();
  43. flags = FLAG_VERBOSE | FLAG_SYNCAGENT; // verbose log
  44. sk_init();
  45. AnsiString VersionString = AnsiString(SshVersionString());
  46. DebugAssert(!VersionString.IsEmpty() && (static_cast<size_t>(VersionString.Length()) < LENOF(sshver)));
  47. strcpy(sshver, VersionString.c_str());
  48. AnsiString AppName = AnsiString(AppNameString());
  49. DebugAssert(!AppName.IsEmpty() && (static_cast<size_t>(AppName.Length()) < LENOF(appname_)));
  50. strcpy(appname_, AppName.c_str());
  51. }
  52. //---------------------------------------------------------------------------
  53. static bool DeleteRandomSeedOnExit()
  54. {
  55. return !HadRandomSeed && !SaveRandomSeed;
  56. }
  57. //---------------------------------------------------------------------------
  58. void __fastcall PuttyFinalize()
  59. {
  60. if (SaveRandomSeed)
  61. {
  62. random_save_seed();
  63. }
  64. random_unref();
  65. // random_ref in PuttyInitialize creates the seed file. Delete it, if we didn't want to create it.
  66. if (DeleteRandomSeedOnExit())
  67. {
  68. DeleteFile(ApiPath(Configuration->RandomSeedFileName));
  69. }
  70. sk_cleanup();
  71. win_misc_cleanup();
  72. win_secur_cleanup();
  73. ec_cleanup();
  74. DeleteCriticalSection(&putty_section);
  75. }
  76. //---------------------------------------------------------------------------
  77. void __fastcall DontSaveRandomSeed()
  78. {
  79. SaveRandomSeed = false;
  80. }
  81. //---------------------------------------------------------------------------
  82. bool RandomSeedExists()
  83. {
  84. return
  85. !DeleteRandomSeedOnExit() &&
  86. FileExists(ApiPath(Configuration->RandomSeedFileName));
  87. }
  88. //---------------------------------------------------------------------------
  89. TSecureShell * GetSecureShell(Plug * plug, bool & pfwd)
  90. {
  91. if (!is_ssh(plug) && !is_pfwd(plug))
  92. {
  93. // If it is not SSH/PFwd plug, then it must be Proxy plug.
  94. // Get SSH/PFwd plug which it wraps.
  95. ProxySocket * AProxySocket = get_proxy_plug_socket(plug);
  96. plug = AProxySocket->plug;
  97. }
  98. pfwd = is_pfwd(plug);
  99. Seat * seat;
  100. if (pfwd)
  101. {
  102. seat = get_pfwd_seat(plug);
  103. }
  104. else
  105. {
  106. seat = get_ssh_seat(plug);
  107. }
  108. DebugAssert(seat != NULL);
  109. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  110. return SecureShell;
  111. }
  112. //---------------------------------------------------------------------------
  113. struct callback_set * get_callback_set(Plug * plug)
  114. {
  115. bool pfwd;
  116. TSecureShell * SecureShell = GetSecureShell(plug, pfwd);
  117. return SecureShell->GetCallbackSet();
  118. }
  119. //---------------------------------------------------------------------------
  120. struct callback_set * get_seat_callback_set(Seat * seat)
  121. {
  122. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  123. return SecureShell->GetCallbackSet();
  124. }
  125. //---------------------------------------------------------------------------
  126. extern "C" char * do_select(Plug * plug, SOCKET skt, bool startup)
  127. {
  128. bool pfwd;
  129. TSecureShell * SecureShell = GetSecureShell(plug, pfwd);
  130. if (!pfwd)
  131. {
  132. SecureShell->UpdateSocket(skt, startup);
  133. }
  134. else
  135. {
  136. SecureShell->UpdatePortFwdSocket(skt, startup);
  137. }
  138. return NULL;
  139. }
  140. //---------------------------------------------------------------------------
  141. static size_t output(Seat * seat, bool is_stderr, const void * data, size_t len)
  142. {
  143. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  144. if (static_cast<int>(static_cast<char>(is_stderr)) == -1)
  145. {
  146. SecureShell->CWrite(reinterpret_cast<const char *>(data), len);
  147. }
  148. else if (!is_stderr)
  149. {
  150. SecureShell->FromBackend(reinterpret_cast<const unsigned char *>(data), len);
  151. }
  152. else
  153. {
  154. SecureShell->AddStdError(reinterpret_cast<const char *>(data), len);
  155. }
  156. return 0;
  157. }
  158. //---------------------------------------------------------------------------
  159. static bool eof(Seat * seat)
  160. {
  161. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  162. return SecureShell->EofReceived();
  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(Configuration != NULL);
  396. DebugAssert(Key == HKEY_CURRENT_USER);
  397. DebugUsedParam(Key);
  398. UnicodeString RegKey = SubKey;
  399. int PuttyKeyLen = OriginalPuttyRegistryStorageKey.Length();
  400. DebugAssert(RegKey.SubString(1, PuttyKeyLen) == OriginalPuttyRegistryStorageKey);
  401. RegKey = RegKey.SubString(PuttyKeyLen + 1, RegKey.Length() - PuttyKeyLen);
  402. if (!RegKey.IsEmpty())
  403. {
  404. DebugAssert(RegKey[1] == L'\\');
  405. RegKey.Delete(1, 1);
  406. }
  407. if (RegKey.IsEmpty())
  408. {
  409. *Result = static_cast<HKEY>(NULL);
  410. R = ERROR_SUCCESS;
  411. }
  412. else
  413. {
  414. // we expect this to be called only from verify_host_key() or store_host_key()
  415. DebugAssert(RegKey == L"SshHostKeys");
  416. THierarchicalStorage * Storage = Configuration->CreateConfigStorage();
  417. Storage->AccessMode = (CanCreate ? smReadWrite : smRead);
  418. if (Storage->OpenSubKey(RegKey, CanCreate))
  419. {
  420. *Result = reinterpret_cast<HKEY>(Storage);
  421. R = ERROR_SUCCESS;
  422. }
  423. else
  424. {
  425. delete Storage;
  426. R = ERROR_CANTOPEN;
  427. }
  428. }
  429. return R;
  430. }
  431. //---------------------------------------------------------------------------
  432. long reg_open_winscp_key(HKEY Key, const char * SubKey, HKEY * Result)
  433. {
  434. if (PuttyRegistryMode == prmPass)
  435. {
  436. return RegOpenKeyA(Key, SubKey, Result);
  437. }
  438. else if (PuttyRegistryMode == prmCollect)
  439. {
  440. *Result = reinterpret_cast<HKEY>(1);
  441. return ERROR_SUCCESS;
  442. }
  443. else if (PuttyRegistryMode == prmFail)
  444. {
  445. return ERROR_CANTOPEN;
  446. }
  447. DebugAssert(PuttyRegistryMode == prmRedirect);
  448. return OpenWinSCPKey(Key, SubKey, Result, false);
  449. }
  450. //---------------------------------------------------------------------------
  451. long reg_create_winscp_key(HKEY Key, const char * SubKey, HKEY * Result)
  452. {
  453. if (PuttyRegistryMode == prmPass)
  454. {
  455. return RegCreateKeyA(Key, SubKey, Result);
  456. }
  457. else if (PuttyRegistryMode == prmCollect)
  458. {
  459. *Result = reinterpret_cast<HKEY>(1);
  460. return ERROR_SUCCESS;
  461. }
  462. DebugAssert(PuttyRegistryMode == prmRedirect);
  463. return OpenWinSCPKey(Key, SubKey, Result, true);
  464. }
  465. //---------------------------------------------------------------------------
  466. long reg_query_winscp_value_ex(HKEY Key, const char * ValueName, unsigned long * Reserved,
  467. unsigned long * Type, unsigned char * Data, unsigned long * DataSize)
  468. {
  469. if (PuttyRegistryMode == prmPass)
  470. {
  471. return RegQueryValueExA(Key, ValueName, Reserved, Type, Data, DataSize);
  472. }
  473. else if (PuttyRegistryMode == prmCollect)
  474. {
  475. return ERROR_READ_FAULT;
  476. }
  477. DebugAssert(PuttyRegistryMode == prmRedirect);
  478. long R;
  479. DebugAssert(Configuration != NULL);
  480. THierarchicalStorage * Storage = reinterpret_cast<THierarchicalStorage *>(Key);
  481. AnsiString Value;
  482. if (Storage == NULL)
  483. {
  484. if (UnicodeString(ValueName) == L"RandSeedFile")
  485. {
  486. Value = AnsiString(Configuration->RandomSeedFileName);
  487. R = ERROR_SUCCESS;
  488. }
  489. else
  490. {
  491. DebugFail();
  492. R = ERROR_READ_FAULT;
  493. }
  494. }
  495. else
  496. {
  497. if (Storage->ValueExists(ValueName))
  498. {
  499. Value = AnsiString(Storage->ReadStringRaw(ValueName, L""));
  500. R = ERROR_SUCCESS;
  501. }
  502. else
  503. {
  504. R = ERROR_READ_FAULT;
  505. }
  506. }
  507. if (R == ERROR_SUCCESS)
  508. {
  509. DebugAssert(Type != NULL);
  510. *Type = REG_SZ;
  511. char * DataStr = reinterpret_cast<char *>(Data);
  512. strncpy(DataStr, Value.c_str(), *DataSize);
  513. DataStr[*DataSize - 1] = '\0';
  514. *DataSize = strlen(DataStr);
  515. }
  516. return R;
  517. }
  518. //---------------------------------------------------------------------------
  519. long reg_set_winscp_value_ex(HKEY Key, const char * ValueName, unsigned long Reserved,
  520. unsigned long Type, const unsigned char * Data, unsigned long DataSize)
  521. {
  522. if (PuttyRegistryMode == prmPass)
  523. {
  524. return RegSetValueExA(Key, ValueName, Reserved, Type, Data, DataSize);
  525. }
  526. else if (PuttyRegistryMode == prmCollect)
  527. {
  528. PuttyRegistryTypes[ValueName] = Type;
  529. return ERROR_SUCCESS;
  530. }
  531. DebugAssert(PuttyRegistryMode == prmRedirect);
  532. DebugAssert(Configuration != NULL);
  533. DebugAssert(Type == REG_SZ);
  534. DebugUsedParam(Type);
  535. THierarchicalStorage * Storage = reinterpret_cast<THierarchicalStorage *>(Key);
  536. DebugAssert(Storage != NULL);
  537. if (Storage != NULL)
  538. {
  539. UnicodeString Value(reinterpret_cast<const char*>(Data), DataSize - 1);
  540. Storage->WriteStringRaw(ValueName, Value);
  541. }
  542. return ERROR_SUCCESS;
  543. }
  544. //---------------------------------------------------------------------------
  545. long reg_close_winscp_key(HKEY Key)
  546. {
  547. if (PuttyRegistryMode == prmPass)
  548. {
  549. return RegCloseKey(Key);
  550. }
  551. else if (PuttyRegistryMode == prmCollect)
  552. {
  553. return ERROR_SUCCESS;
  554. }
  555. DebugAssert(PuttyRegistryMode == prmRedirect);
  556. DebugAssert(Configuration != NULL);
  557. THierarchicalStorage * Storage = reinterpret_cast<THierarchicalStorage *>(Key);
  558. if (Storage != NULL)
  559. {
  560. delete Storage;
  561. }
  562. return ERROR_SUCCESS;
  563. }
  564. //---------------------------------------------------------------------------
  565. TKeyType KeyType(UnicodeString FileName)
  566. {
  567. DebugAssert(ktUnopenable == SSH_KEYTYPE_UNOPENABLE);
  568. DebugAssert(ktSSHCom == SSH_KEYTYPE_SSHCOM);
  569. DebugAssert(ktSSH2PublicOpenSSH == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH);
  570. UTF8String UtfFileName = UTF8String(FileName);
  571. Filename * KeyFile = filename_from_str(UtfFileName.c_str());
  572. TKeyType Result = (TKeyType)key_type(KeyFile);
  573. filename_free(KeyFile);
  574. return Result;
  575. }
  576. //---------------------------------------------------------------------------
  577. bool IsKeyEncrypted(TKeyType KeyType, const UnicodeString & FileName, UnicodeString & Comment)
  578. {
  579. UTF8String UtfFileName = UTF8String(FileName);
  580. bool Result;
  581. char * CommentStr = NULL;
  582. Filename * KeyFile = filename_from_str(UtfFileName.c_str());
  583. try
  584. {
  585. switch (KeyType)
  586. {
  587. case ktSSH2:
  588. Result = (ssh2_userkey_encrypted(KeyFile, &CommentStr) != 0);
  589. break;
  590. case ktOpenSSHPEM:
  591. case ktOpenSSHNew:
  592. case ktSSHCom:
  593. Result = (import_encrypted(KeyFile, KeyType, &CommentStr) != NULL);
  594. break;
  595. default:
  596. DebugFail();
  597. Result = false;
  598. break;
  599. }
  600. }
  601. __finally
  602. {
  603. filename_free(KeyFile);
  604. }
  605. if (CommentStr != NULL)
  606. {
  607. Comment = UnicodeString(AnsiString(CommentStr));
  608. // ktOpenSSH has no comment, PuTTY defaults to file path
  609. if (Comment == FileName)
  610. {
  611. Comment = ExtractFileName(FileName);
  612. }
  613. sfree(CommentStr);
  614. }
  615. return Result;
  616. }
  617. //---------------------------------------------------------------------------
  618. TPrivateKey * LoadKey(TKeyType KeyType, const UnicodeString & FileName, const UnicodeString & Passphrase)
  619. {
  620. UTF8String UtfFileName = UTF8String(FileName);
  621. Filename * KeyFile = filename_from_str(UtfFileName.c_str());
  622. struct ssh2_userkey * Ssh2Key = NULL;
  623. const char * ErrorStr = NULL;
  624. AnsiString AnsiPassphrase = Passphrase;
  625. try
  626. {
  627. switch (KeyType)
  628. {
  629. case ktSSH2:
  630. Ssh2Key = ssh2_load_userkey(KeyFile, AnsiPassphrase.c_str(), &ErrorStr);
  631. break;
  632. case ktOpenSSHPEM:
  633. case ktOpenSSHNew:
  634. case ktSSHCom:
  635. Ssh2Key = import_ssh2(KeyFile, KeyType, AnsiPassphrase.c_str(), &ErrorStr);
  636. break;
  637. default:
  638. DebugFail();
  639. break;
  640. }
  641. }
  642. __finally
  643. {
  644. Shred(AnsiPassphrase);
  645. filename_free(KeyFile);
  646. }
  647. if (Ssh2Key == NULL)
  648. {
  649. UnicodeString Error = AnsiString(ErrorStr);
  650. // While theoretically we may get "unable to open key file" and
  651. // so we should check system error code,
  652. // we actully never get here unless we call KeyType previously
  653. // and handle ktUnopenable accordingly.
  654. throw Exception(Error);
  655. }
  656. else if (Ssh2Key == SSH2_WRONG_PASSPHRASE)
  657. {
  658. throw Exception(LoadStr(AUTH_TRANSL_WRONG_PASSPHRASE));
  659. }
  660. return reinterpret_cast<TPrivateKey *>(Ssh2Key);
  661. }
  662. //---------------------------------------------------------------------------
  663. void ChangeKeyComment(TPrivateKey * PrivateKey, const UnicodeString & Comment)
  664. {
  665. AnsiString AnsiComment(Comment);
  666. struct ssh2_userkey * Ssh2Key = reinterpret_cast<struct ssh2_userkey *>(PrivateKey);
  667. sfree(Ssh2Key->comment);
  668. Ssh2Key->comment = dupstr(AnsiComment.c_str());
  669. }
  670. //---------------------------------------------------------------------------
  671. void SaveKey(TKeyType KeyType, const UnicodeString & FileName,
  672. const UnicodeString & Passphrase, TPrivateKey * PrivateKey)
  673. {
  674. UTF8String UtfFileName = UTF8String(FileName);
  675. Filename * KeyFile = filename_from_str(UtfFileName.c_str());
  676. try
  677. {
  678. struct ssh2_userkey * Ssh2Key = reinterpret_cast<struct ssh2_userkey *>(PrivateKey);
  679. AnsiString AnsiPassphrase = Passphrase;
  680. char * PassphrasePtr = (AnsiPassphrase.IsEmpty() ? NULL : AnsiPassphrase.c_str());
  681. switch (KeyType)
  682. {
  683. case ktSSH2:
  684. if (!ssh2_save_userkey(KeyFile, Ssh2Key, PassphrasePtr))
  685. {
  686. int Error = errno;
  687. throw EOSExtException(FMTLOAD(KEY_SAVE_ERROR, (FileName)), Error);
  688. }
  689. break;
  690. default:
  691. DebugFail();
  692. break;
  693. }
  694. }
  695. __finally
  696. {
  697. filename_free(KeyFile);
  698. }
  699. }
  700. //---------------------------------------------------------------------------
  701. void FreeKey(TPrivateKey * PrivateKey)
  702. {
  703. struct ssh2_userkey * Ssh2Key = reinterpret_cast<struct ssh2_userkey *>(PrivateKey);
  704. ssh_key_free(Ssh2Key->key);
  705. sfree(Ssh2Key);
  706. }
  707. //---------------------------------------------------------------------------
  708. RawByteString LoadPublicKey(const UnicodeString & FileName, UnicodeString & Algorithm, UnicodeString & Comment)
  709. {
  710. RawByteString Result;
  711. UTF8String UtfFileName = UTF8String(FileName);
  712. Filename * KeyFile = filename_from_str(UtfFileName.c_str());
  713. try
  714. {
  715. char * AlgorithmStr = NULL;
  716. char * CommentStr = NULL;
  717. const char * ErrorStr = NULL;
  718. strbuf * PublicKeyBuf = strbuf_new();
  719. if (!ssh2_userkey_loadpub(KeyFile, &AlgorithmStr, BinarySink_UPCAST(PublicKeyBuf), &CommentStr, &ErrorStr))
  720. {
  721. UnicodeString Error = UnicodeString(AnsiString(ErrorStr));
  722. throw Exception(Error);
  723. }
  724. Algorithm = UnicodeString(AnsiString(AlgorithmStr));
  725. sfree(AlgorithmStr);
  726. Comment = UnicodeString(AnsiString(CommentStr));
  727. sfree(CommentStr);
  728. Result = RawByteString(reinterpret_cast<char *>(PublicKeyBuf->s), PublicKeyBuf->len);
  729. strbuf_free(PublicKeyBuf);
  730. }
  731. __finally
  732. {
  733. filename_free(KeyFile);
  734. }
  735. return Result;
  736. }
  737. //---------------------------------------------------------------------------
  738. UnicodeString GetPublicKeyLine(const UnicodeString & FileName, UnicodeString & Comment)
  739. {
  740. UnicodeString Algorithm;
  741. RawByteString PublicKey = LoadPublicKey(FileName, Algorithm, Comment);
  742. UnicodeString PublicKeyBase64 = EncodeBase64(PublicKey.c_str(), PublicKey.Length());
  743. PublicKeyBase64 = ReplaceStr(PublicKeyBase64, L"\r", L"");
  744. PublicKeyBase64 = ReplaceStr(PublicKeyBase64, L"\n", L"");
  745. UnicodeString Result = FORMAT(L"%s %s %s", (Algorithm, PublicKeyBase64, Comment));
  746. return Result;
  747. }
  748. //---------------------------------------------------------------------------
  749. bool __fastcall HasGSSAPI(UnicodeString CustomPath)
  750. {
  751. static int has = -1;
  752. if (has < 0)
  753. {
  754. Conf * conf = conf_new();
  755. ssh_gss_liblist * List = NULL;
  756. try
  757. {
  758. Filename * filename = filename_from_str(UTF8String(CustomPath).c_str());
  759. conf_set_filename(conf, CONF_ssh_gss_custom, filename);
  760. filename_free(filename);
  761. List = ssh_gss_setup(conf, NULL);
  762. for (int Index = 0; (has <= 0) && (Index < List->nlibraries); Index++)
  763. {
  764. ssh_gss_library * library = &List->libraries[Index];
  765. Ssh_gss_ctx ctx;
  766. memset(&ctx, 0, sizeof(ctx));
  767. has =
  768. ((library->acquire_cred(library, &ctx, NULL) == SSH_GSS_OK) &&
  769. (library->release_cred(library, &ctx) == SSH_GSS_OK)) ? 1 : 0;
  770. }
  771. }
  772. __finally
  773. {
  774. ssh_gss_cleanup(List);
  775. conf_free(conf);
  776. }
  777. if (has < 0)
  778. {
  779. has = 0;
  780. }
  781. }
  782. return (has > 0);
  783. }
  784. //---------------------------------------------------------------------------
  785. static void __fastcall DoNormalizeFingerprint(UnicodeString & Fingerprint, UnicodeString & KeyName, UnicodeString & KeyType)
  786. {
  787. const int MaxCount = 10;
  788. const ssh_keyalg * SignKeys[MaxCount];
  789. int Count = LENOF(SignKeys);
  790. // We may use find_pubkey_alg, but it gets complicated with normalized fingerprint
  791. // as the names have different number of dashes
  792. get_hostkey_algs(&Count, SignKeys);
  793. for (int Index = 0; Index < Count; Index++)
  794. {
  795. const ssh_keyalg * SignKey = SignKeys[Index];
  796. UnicodeString Name = UnicodeString(SignKey->ssh_id);
  797. if (StartsStr(Name + L" ", Fingerprint))
  798. {
  799. UnicodeString Rest = Fingerprint.SubString(Name.Length() + 2, Fingerprint.Length() - Name.Length() - 1);
  800. int Space = Rest.Pos(L" ");
  801. // If not a number, it's an invalid input,
  802. // either something completelly wrong, or it can be OpenSSH base64 public key,
  803. // that got here from TPasteKeyHandler::Paste
  804. if (IsNumber(Rest.SubString(1, Space - 1)))
  805. {
  806. KeyName = Name;
  807. Fingerprint = Rest.SubString(Space + 1, Fingerprint.Length() - Space);
  808. Fingerprint = Base64ToUrlSafe(Fingerprint);
  809. Fingerprint = MD5ToUrlSafe(Fingerprint);
  810. KeyType = UnicodeString(SignKey->cache_id);
  811. return;
  812. }
  813. }
  814. else if (StartsStr(Name + NormalizedFingerprintSeparator, Fingerprint))
  815. {
  816. KeyType = UnicodeString(SignKey->cache_id);
  817. KeyName = Name;
  818. Fingerprint.Delete(1, Name.Length() + 1);
  819. return;
  820. }
  821. }
  822. }
  823. //---------------------------------------------------------------------------
  824. void __fastcall NormalizeFingerprint(UnicodeString & Fingerprint, UnicodeString & KeyName)
  825. {
  826. UnicodeString KeyType;
  827. DoNormalizeFingerprint(Fingerprint, KeyName, KeyType);
  828. }
  829. //---------------------------------------------------------------------------
  830. UnicodeString __fastcall KeyTypeFromFingerprint(UnicodeString Fingerprint)
  831. {
  832. UnicodeString KeyType;
  833. UnicodeString KeyName; // unused
  834. DoNormalizeFingerprint(Fingerprint, KeyName, KeyType);
  835. return KeyType;
  836. }
  837. //---------------------------------------------------------------------------
  838. UnicodeString __fastcall GetPuTTYVersion()
  839. {
  840. // "Release 0.64"
  841. // "Pre-release 0.65:2015-07-20.95501a1"
  842. // "Development snapshot 2015-12-22.51465fa"
  843. UnicodeString Result = get_putty_version();
  844. // Skip "Release", "Pre-release", "Development snapshot"
  845. int P = Result.LastDelimiter(L" ");
  846. Result.Delete(1, P);
  847. return Result;
  848. }
  849. //---------------------------------------------------------------------------
  850. UnicodeString __fastcall Sha256(const char * Data, size_t Size)
  851. {
  852. unsigned char Digest[32];
  853. hash_simple(&ssh_sha256, make_ptrlen(Data, Size), Digest);
  854. UnicodeString Result(BytesToHex(Digest, LENOF(Digest)));
  855. return Result;
  856. }
  857. //---------------------------------------------------------------------------
  858. void __fastcall DllHijackingProtection()
  859. {
  860. dll_hijacking_protection();
  861. }
  862. //---------------------------------------------------------------------------
  863. UnicodeString __fastcall ParseOpenSshPubLine(const UnicodeString & Line, const struct ssh_keyalg *& Algorithm)
  864. {
  865. UTF8String UtfLine = UTF8String(Line);
  866. char * AlgorithmName = NULL;
  867. char * CommentPtr = NULL;
  868. const char * ErrorStr = NULL;
  869. strbuf * PubBlobBuf = strbuf_new();
  870. UnicodeString Result;
  871. if (!openssh_loadpub_line(UtfLine.c_str(), &AlgorithmName, BinarySink_UPCAST(PubBlobBuf), &CommentPtr, &ErrorStr))
  872. {
  873. throw Exception(UnicodeString(ErrorStr));
  874. }
  875. else
  876. {
  877. try
  878. {
  879. Algorithm = find_pubkey_alg(AlgorithmName);
  880. if (Algorithm == NULL)
  881. {
  882. throw Exception(FORMAT(L"Unknown public key algorithm \"%s\".", (AlgorithmName)));
  883. }
  884. ptrlen PtrLen = { PubBlobBuf->s, PubBlobBuf->len };
  885. ssh_key * Key = Algorithm->new_pub(Algorithm, PtrLen);
  886. if (Key == NULL)
  887. {
  888. throw Exception(L"Invalid public key.");
  889. }
  890. char * FmtKey = Algorithm->cache_str(Key);
  891. Result = UnicodeString(FmtKey);
  892. sfree(FmtKey);
  893. Algorithm->freekey(Key);
  894. }
  895. __finally
  896. {
  897. strbuf_free(PubBlobBuf);
  898. sfree(AlgorithmName);
  899. sfree(CommentPtr);
  900. }
  901. }
  902. return Result;
  903. }
  904. //---------------------------------------------------------------------------
  905. UnicodeString __fastcall GetSsh1KeyType()
  906. {
  907. return UnicodeString(ssh_rsa.cache_id);
  908. }
  909. //---------------------------------------------------------------------------
  910. UnicodeString __fastcall GetKeyTypeHuman(const UnicodeString & KeyType)
  911. {
  912. UnicodeString Result;
  913. if (KeyType == ssh_dss.cache_id)
  914. {
  915. Result = L"DSA";
  916. }
  917. else if ((KeyType == ssh_rsa.cache_id) ||
  918. (KeyType == L"rsa")) // SSH1
  919. {
  920. Result = L"RSA";
  921. }
  922. else if (KeyType == ssh_ecdsa_ed25519.cache_id)
  923. {
  924. Result = L"Ed25519";
  925. }
  926. else if (KeyType == ssh_ecdsa_nistp256.cache_id)
  927. {
  928. Result = L"ECDSA/nistp256";
  929. }
  930. else if (KeyType == ssh_ecdsa_nistp384.cache_id)
  931. {
  932. Result = L"ECDSA/nistp384";
  933. }
  934. else if (KeyType == ssh_ecdsa_nistp521.cache_id)
  935. {
  936. Result = L"ECDSA/nistp521";
  937. }
  938. else
  939. {
  940. DebugFail();
  941. Result = KeyType;
  942. }
  943. return Result;
  944. }
  945. //---------------------------------------------------------------------------
  946. bool IsOpenSSH(const UnicodeString & SshImplementation)
  947. {
  948. return
  949. // e.g. "OpenSSH_5.3"
  950. (SshImplementation.Pos(L"OpenSSH") == 1) ||
  951. // Sun SSH is based on OpenSSH (suffers the same bugs)
  952. (SshImplementation.Pos(L"Sun_SSH") == 1);
  953. }
  954. //---------------------------------------------------------------------------
  955. TStrings * SshCipherList()
  956. {
  957. std::unique_ptr<TStrings> Result(new TStringList());
  958. // Same order as DefaultCipherList
  959. const ssh2_ciphers * Ciphers[] = { &ssh2_aes, &ssh2_ccp, &ssh2_blowfish, &ssh2_3des, &ssh2_arcfour, &ssh2_des };
  960. for (unsigned int Index = 0; Index < LENOF(Ciphers); Index++)
  961. {
  962. for (int Index2 = 0; Index2 < Ciphers[Index]->nciphers; Index2++)
  963. {
  964. UnicodeString Name = UnicodeString(Ciphers[Index]->list[Index2]->ssh2_id);
  965. Result->Add(Name);
  966. }
  967. }
  968. return Result.release();
  969. }
  970. //---------------------------------------------------------------------------
  971. TStrings * SshKexList()
  972. {
  973. std::unique_ptr<TStrings> Result(new TStringList());
  974. // Same order as DefaultKexList
  975. const ssh_kexes * Kexes[] = { &ssh_ecdh_kex, &ssh_diffiehellman_gex, &ssh_diffiehellman_group14, &ssh_rsa_kex, &ssh_diffiehellman_group1 };
  976. for (unsigned int Index = 0; Index < LENOF(Kexes); Index++)
  977. {
  978. for (int Index2 = 0; Index2 < Kexes[Index]->nkexes; Index2++)
  979. {
  980. UnicodeString Name = UnicodeString(Kexes[Index]->list[Index2]->name);
  981. Result->Add(Name);
  982. }
  983. }
  984. return Result.release();
  985. }
  986. //---------------------------------------------------------------------------
  987. TStrings * SshHostKeyList()
  988. {
  989. std::unique_ptr<TStrings> Result(new TStringList());
  990. const int MaxCount = 10;
  991. const ssh_keyalg * SignKeys[MaxCount];
  992. int Count = LENOF(SignKeys);
  993. get_hostkey_algs(&Count, SignKeys);
  994. for (int Index = 0; Index < Count; Index++)
  995. {
  996. const ssh_keyalg * SignKey = SignKeys[Index];
  997. UnicodeString Name = UnicodeString(SignKey->ssh_id);
  998. Result->Add(Name);
  999. }
  1000. return Result.release();
  1001. }
  1002. //---------------------------------------------------------------------------
  1003. TStrings * SshMacList()
  1004. {
  1005. std::unique_ptr<TStrings> Result(new TStringList());
  1006. const struct ssh2_macalg ** Macs = NULL;
  1007. int Count = 0;
  1008. get_macs(&Count, &Macs);
  1009. for (int Index = 0; Index < Count; Index++)
  1010. {
  1011. UnicodeString Name = UnicodeString(Macs[Index]->name);
  1012. Result->Add(Name);
  1013. }
  1014. return Result.release();
  1015. }
  1016. //---------------------------------------------------------------------------
  1017. UnicodeString GetCipherName(const ssh_cipher * Cipher)
  1018. {
  1019. return UnicodeString(UTF8String(Cipher->vt->text_name));
  1020. }
  1021. //---------------------------------------------------------------------------
  1022. UnicodeString GetCompressorName(const ssh_compressor * Compressor)
  1023. {
  1024. UnicodeString Result;
  1025. if (Compressor != NULL)
  1026. {
  1027. Result = UnicodeString(UTF8String(Compressor->vt->name));
  1028. }
  1029. return Result;
  1030. }
  1031. //---------------------------------------------------------------------------
  1032. UnicodeString GetDecompressorName(const ssh_decompressor * Decompressor)
  1033. {
  1034. UnicodeString Result;
  1035. if (Decompressor != NULL)
  1036. {
  1037. Result = UnicodeString(UTF8String(Decompressor->vt->name));
  1038. }
  1039. return Result;
  1040. }
  1041. //---------------------------------------------------------------------------
  1042. void WritePuttySettings(THierarchicalStorage * Storage, const UnicodeString & ASettings)
  1043. {
  1044. if (PuttyRegistryTypes.empty())
  1045. {
  1046. TGuard Guard(PuttyRegistrySection.get());
  1047. TValueRestorer<TPuttyRegistryMode> PuttyRegistryModeRestorer(PuttyRegistryMode);
  1048. PuttyRegistryMode = prmCollect;
  1049. Conf * conf = conf_new();
  1050. try
  1051. {
  1052. do_defaults(NULL, conf);
  1053. save_settings(NULL, conf);
  1054. }
  1055. __finally
  1056. {
  1057. conf_free(conf);
  1058. }
  1059. }
  1060. std::unique_ptr<TStrings> Settings(new TStringList());
  1061. UnicodeString Buf = ASettings;
  1062. UnicodeString Setting;
  1063. while (CutToken(Buf, Setting))
  1064. {
  1065. Settings->Add(Setting);
  1066. }
  1067. for (int Index = 0; Index < Settings->Count; Index++)
  1068. {
  1069. UnicodeString Name = Settings->Names[Index];
  1070. TPuttyRegistryTypes::const_iterator IType = PuttyRegistryTypes.find(Name);
  1071. if (IType != PuttyRegistryTypes.end())
  1072. {
  1073. UnicodeString Value = Settings->ValueFromIndex[Index];
  1074. int I;
  1075. if (IType->second == REG_SZ)
  1076. {
  1077. Storage->WriteStringRaw(Name, Value);
  1078. }
  1079. else if (DebugAlwaysTrue(IType->second == REG_DWORD) &&
  1080. TryStrToInt(Value, I))
  1081. {
  1082. Storage->WriteInteger(Name, I);
  1083. }
  1084. }
  1085. }
  1086. }
  1087. //---------------------------------------------------------------------------
  1088. void PuttyDefaults(Conf * conf)
  1089. {
  1090. TGuard Guard(PuttyRegistrySection.get());
  1091. TValueRestorer<TPuttyRegistryMode> PuttyRegistryModeRestorer(PuttyRegistryMode);
  1092. PuttyRegistryMode = prmFail;
  1093. do_defaults(NULL, conf);
  1094. }
  1095. //---------------------------------------------------------------------------
  1096. void SavePuttyDefaults(const UnicodeString & Name)
  1097. {
  1098. TGuard Guard(PuttyRegistrySection.get());
  1099. TValueRestorer<TPuttyRegistryMode> PuttyRegistryModeRestorer(PuttyRegistryMode);
  1100. PuttyRegistryMode = prmPass;
  1101. Conf * conf = conf_new();
  1102. try
  1103. {
  1104. PuttyDefaults(conf);
  1105. AnsiString PuttyName = PuttyStr(Name);
  1106. save_settings(PuttyName.c_str(), conf);
  1107. }
  1108. __finally
  1109. {
  1110. conf_free(conf);
  1111. }
  1112. }
  1113. //---------------------------------------------------------------------------
  1114. //---------------------------------------------------------------------------