PuttyIntf.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  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. //---------------------------------------------------------------------------
  13. char sshver[50];
  14. extern const char commitid[] = "";
  15. const int platform_uses_x11_unix_by_default = TRUE;
  16. CRITICAL_SECTION putty_section;
  17. bool SaveRandomSeed;
  18. char appname_[50];
  19. const char *const appname = appname_;
  20. extern const int share_can_be_downstream = FALSE;
  21. extern const int share_can_be_upstream = FALSE;
  22. //---------------------------------------------------------------------------
  23. extern "C"
  24. {
  25. #include <winstuff.h>
  26. }
  27. const UnicodeString OriginalPuttyRegistryStorageKey(_T(PUTTY_REG_POS));
  28. const UnicodeString KittyRegistryStorageKey(L"Software\\9bis.com\\KiTTY");
  29. const UnicodeString OriginalPuttyExecutable("putty.exe");
  30. const UnicodeString KittyExecutable("kitty.exe");
  31. //---------------------------------------------------------------------------
  32. void __fastcall PuttyInitialize()
  33. {
  34. SaveRandomSeed = true;
  35. InitializeCriticalSection(&putty_section);
  36. // make sure random generator is initialised, so random_save_seed()
  37. // in destructor can proceed
  38. random_ref();
  39. flags = FLAG_VERBOSE | FLAG_SYNCAGENT; // verbose log
  40. sk_init();
  41. AnsiString VersionString = AnsiString(SshVersionString());
  42. DebugAssert(!VersionString.IsEmpty() && (static_cast<size_t>(VersionString.Length()) < LENOF(sshver)));
  43. strcpy(sshver, VersionString.c_str());
  44. AnsiString AppName = AnsiString(AppNameString());
  45. DebugAssert(!AppName.IsEmpty() && (static_cast<size_t>(AppName.Length()) < LENOF(appname_)));
  46. strcpy(appname_, AppName.c_str());
  47. }
  48. //---------------------------------------------------------------------------
  49. void __fastcall PuttyFinalize()
  50. {
  51. if (SaveRandomSeed)
  52. {
  53. random_save_seed();
  54. }
  55. random_unref();
  56. sk_cleanup();
  57. win_misc_cleanup();
  58. win_secur_cleanup();
  59. ec_cleanup();
  60. DeleteCriticalSection(&putty_section);
  61. }
  62. //---------------------------------------------------------------------------
  63. void __fastcall DontSaveRandomSeed()
  64. {
  65. SaveRandomSeed = false;
  66. }
  67. //---------------------------------------------------------------------------
  68. extern "C" char * do_select(Plug plug, SOCKET skt, int startup)
  69. {
  70. void * frontend;
  71. if (!is_ssh(plug) && !is_pfwd(plug))
  72. {
  73. // If it is not SSH/PFwd plug, then it must be Proxy plug.
  74. // Get SSH/PFwd plug which it wraps.
  75. Proxy_Socket ProxySocket = ((Proxy_Plug)plug)->proxy_socket;
  76. plug = ProxySocket->plug;
  77. }
  78. bool pfwd = is_pfwd(plug);
  79. if (pfwd)
  80. {
  81. plug = (Plug)get_pfwd_backend(plug);
  82. }
  83. frontend = get_ssh_frontend(plug);
  84. DebugAssert(frontend);
  85. TSecureShell * SecureShell = reinterpret_cast<TSecureShell*>(frontend);
  86. if (!pfwd)
  87. {
  88. SecureShell->UpdateSocket(skt, startup);
  89. }
  90. else
  91. {
  92. SecureShell->UpdatePortFwdSocket(skt, startup);
  93. }
  94. return NULL;
  95. }
  96. //---------------------------------------------------------------------------
  97. int from_backend(void * frontend, int is_stderr, const char * data, int datalen)
  98. {
  99. DebugAssert(frontend);
  100. if (is_stderr >= 0)
  101. {
  102. DebugAssert((is_stderr == 0) || (is_stderr == 1));
  103. ((TSecureShell *)frontend)->FromBackend((is_stderr == 1), reinterpret_cast<const unsigned char *>(data), datalen);
  104. }
  105. else
  106. {
  107. DebugAssert(is_stderr == -1);
  108. ((TSecureShell *)frontend)->CWrite(data, datalen);
  109. }
  110. return 0;
  111. }
  112. //---------------------------------------------------------------------------
  113. int from_backend_untrusted(void * /*frontend*/, const char * /*data*/, int /*len*/)
  114. {
  115. // currently used with authentication banner only,
  116. // for which we have own interface display_banner
  117. return 0;
  118. }
  119. //---------------------------------------------------------------------------
  120. int from_backend_eof(void * /*frontend*/)
  121. {
  122. return FALSE;
  123. }
  124. //---------------------------------------------------------------------------
  125. int get_userpass_input(prompts_t * p, const unsigned char * /*in*/, int /*inlen*/)
  126. {
  127. DebugAssert(p != NULL);
  128. TSecureShell * SecureShell = reinterpret_cast<TSecureShell *>(p->frontend);
  129. DebugAssert(SecureShell != NULL);
  130. int Result;
  131. TStrings * Prompts = new TStringList();
  132. TStrings * Results = new TStringList();
  133. try
  134. {
  135. UnicodeString Name = UTF8ToString(p->name);
  136. UnicodeString AName = Name;
  137. TPromptKind PromptKind = SecureShell->IdentifyPromptKind(AName);
  138. bool UTF8Prompt = (PromptKind != pkPassphrase);
  139. for (int Index = 0; Index < int(p->n_prompts); Index++)
  140. {
  141. prompt_t * Prompt = p->prompts[Index];
  142. UnicodeString S;
  143. if (UTF8Prompt)
  144. {
  145. S = UTF8ToString(Prompt->prompt);
  146. }
  147. else
  148. {
  149. S = UnicodeString(AnsiString(Prompt->prompt));
  150. }
  151. Prompts->AddObject(S, (TObject *)(FLAGMASK(Prompt->echo, pupEcho)));
  152. // this fails, when new passwords do not match on change password prompt,
  153. // and putty retries the prompt
  154. DebugAssert(Prompt->resultsize == 0);
  155. Results->Add(L"");
  156. }
  157. UnicodeString Instructions = UTF8ToString(p->instruction);
  158. if (SecureShell->PromptUser(p->to_server, Name, p->name_reqd,
  159. Instructions, p->instr_reqd, Prompts, Results))
  160. {
  161. for (int Index = 0; Index < int(p->n_prompts); Index++)
  162. {
  163. prompt_t * Prompt = p->prompts[Index];
  164. RawByteString S;
  165. if (UTF8Prompt)
  166. {
  167. S = RawByteString(UTF8String(Results->Strings[Index]));
  168. }
  169. else
  170. {
  171. S = RawByteString(AnsiString(Results->Strings[Index]));
  172. }
  173. prompt_set_result(Prompt, S.c_str());
  174. }
  175. Result = 1;
  176. }
  177. else
  178. {
  179. Result = 0;
  180. }
  181. }
  182. __finally
  183. {
  184. delete Prompts;
  185. delete Results;
  186. }
  187. return Result;
  188. }
  189. //---------------------------------------------------------------------------
  190. char * get_ttymode(void * /*frontend*/, const char * /*mode*/)
  191. {
  192. // should never happen when Config.nopty == TRUE
  193. DebugFail();
  194. return NULL;
  195. }
  196. //---------------------------------------------------------------------------
  197. void logevent(void * frontend, const char * string)
  198. {
  199. // Frontend maybe NULL here
  200. if (frontend != NULL)
  201. {
  202. ((TSecureShell *)frontend)->PuttyLogEvent(string);
  203. }
  204. }
  205. //---------------------------------------------------------------------------
  206. void connection_fatal(void * frontend, const char * fmt, ...)
  207. {
  208. va_list Param;
  209. char Buf[200];
  210. va_start(Param, fmt);
  211. vsnprintf(Buf, LENOF(Buf), fmt, Param); \
  212. Buf[LENOF(Buf) - 1] = '\0'; \
  213. va_end(Param);
  214. DebugAssert(frontend != NULL);
  215. ((TSecureShell *)frontend)->PuttyFatalError(Buf);
  216. }
  217. //---------------------------------------------------------------------------
  218. int verify_ssh_host_key(void * frontend, char * host, int port, const char * keytype,
  219. char * keystr, char * fingerprint, void (*/*callback*/)(void * ctx, int result),
  220. void * /*ctx*/)
  221. {
  222. DebugAssert(frontend != NULL);
  223. static_cast<TSecureShell *>(frontend)->VerifyHostKey(host, port, keytype, keystr, fingerprint);
  224. // We should return 0 when key was not confirmed, we throw exception instead.
  225. return 1;
  226. }
  227. //---------------------------------------------------------------------------
  228. int have_ssh_host_key(void * frontend, const char * hostname, int port,
  229. const char * keytype)
  230. {
  231. DebugAssert(frontend != NULL);
  232. return static_cast<TSecureShell *>(frontend)->HaveHostKey(hostname, port, keytype) ? 1 : 0;
  233. }
  234. //---------------------------------------------------------------------------
  235. int askalg(void * frontend, const char * algtype, const char * algname,
  236. void (*/*callback*/)(void * ctx, int result), void * /*ctx*/)
  237. {
  238. DebugAssert(frontend != NULL);
  239. ((TSecureShell *)frontend)->AskAlg(algtype, algname);
  240. // We should return 0 when alg was not confirmed, we throw exception instead.
  241. return 1;
  242. }
  243. //---------------------------------------------------------------------------
  244. int askhk(void * /*frontend*/, const char * /*algname*/, const char * /*betteralgs*/,
  245. void (*/*callback*/)(void *ctx, int result), void * /*ctx*/)
  246. {
  247. return 1;
  248. }
  249. //---------------------------------------------------------------------------
  250. void old_keyfile_warning(void)
  251. {
  252. // no reference to TSecureShell instance available
  253. }
  254. //---------------------------------------------------------------------------
  255. void display_banner(void * frontend, const char * banner, int size)
  256. {
  257. DebugAssert(frontend);
  258. UnicodeString Banner(UTF8String(banner, size));
  259. ((TSecureShell *)frontend)->DisplayBanner(Banner);
  260. }
  261. //---------------------------------------------------------------------------
  262. static void SSHFatalError(const char * Format, va_list Param)
  263. {
  264. char Buf[200];
  265. vsnprintf(Buf, LENOF(Buf), Format, Param);
  266. Buf[LENOF(Buf) - 1] = '\0';
  267. // Only few calls from putty\winnet.c might be connected with specific
  268. // TSecureShell. Otherwise called only for really fatal errors
  269. // like 'out of memory' from putty\ssh.c.
  270. throw ESshFatal(NULL, Buf);
  271. }
  272. //---------------------------------------------------------------------------
  273. void fatalbox(const char * fmt, ...)
  274. {
  275. va_list Param;
  276. va_start(Param, fmt);
  277. SSHFatalError(fmt, Param);
  278. va_end(Param);
  279. }
  280. //---------------------------------------------------------------------------
  281. void modalfatalbox(const char * fmt, ...)
  282. {
  283. va_list Param;
  284. va_start(Param, fmt);
  285. SSHFatalError(fmt, Param);
  286. va_end(Param);
  287. }
  288. //---------------------------------------------------------------------------
  289. void nonfatal(const char * fmt, ...)
  290. {
  291. va_list Param;
  292. va_start(Param, fmt);
  293. SSHFatalError(fmt, Param);
  294. va_end(Param);
  295. }
  296. //---------------------------------------------------------------------------
  297. void cleanup_exit(int /*code*/)
  298. {
  299. throw ESshFatal(NULL, "");
  300. }
  301. //---------------------------------------------------------------------------
  302. int askappend(void * /*frontend*/, Filename * /*filename*/,
  303. void (*/*callback*/)(void * ctx, int result), void * /*ctx*/)
  304. {
  305. // this is called from logging.c of putty, which is never used with WinSCP
  306. DebugFail();
  307. return 0;
  308. }
  309. //---------------------------------------------------------------------------
  310. void ldisc_echoedit_update(void * /*handle*/)
  311. {
  312. DebugFail();
  313. }
  314. //---------------------------------------------------------------------------
  315. void agent_schedule_callback(void (* /*callback*/)(void *, void *, int),
  316. void * /*callback_ctx*/, void * /*data*/, int /*len*/)
  317. {
  318. DebugFail();
  319. }
  320. //---------------------------------------------------------------------------
  321. void notify_remote_exit(void * /*frontend*/)
  322. {
  323. // nothing
  324. }
  325. //---------------------------------------------------------------------------
  326. void update_specials_menu(void * /*frontend*/)
  327. {
  328. // nothing
  329. }
  330. //---------------------------------------------------------------------------
  331. unsigned long schedule_timer(int ticks, timer_fn_t /*fn*/, void * /*ctx*/)
  332. {
  333. return ticks + GetTickCount();
  334. }
  335. //---------------------------------------------------------------------------
  336. void expire_timer_context(void * /*ctx*/)
  337. {
  338. // nothing
  339. }
  340. //---------------------------------------------------------------------------
  341. Pinger pinger_new(Conf * /*conf*/, Backend * /*back*/, void * /*backhandle*/)
  342. {
  343. return NULL;
  344. }
  345. //---------------------------------------------------------------------------
  346. void pinger_reconfig(Pinger /*pinger*/, Conf * /*oldconf*/, Conf * /*newconf*/)
  347. {
  348. // nothing
  349. }
  350. //---------------------------------------------------------------------------
  351. void pinger_free(Pinger /*pinger*/)
  352. {
  353. // nothing
  354. }
  355. //---------------------------------------------------------------------------
  356. void set_busy_status(void * /*frontend*/, int /*status*/)
  357. {
  358. // nothing
  359. }
  360. //---------------------------------------------------------------------------
  361. void platform_get_x11_auth(struct X11Display * /*display*/, Conf * /*conf*/)
  362. {
  363. // nothing, therefore no auth.
  364. }
  365. //---------------------------------------------------------------------------
  366. // Based on PuTTY's settings.c
  367. char * get_remote_username(Conf * conf)
  368. {
  369. char * username = conf_get_str(conf, CONF_username);
  370. char * result;
  371. if (*username)
  372. {
  373. result = dupstr(username);
  374. }
  375. else
  376. {
  377. result = NULL;
  378. }
  379. return result;
  380. }
  381. //---------------------------------------------------------------------------
  382. static long OpenWinSCPKey(HKEY Key, const char * SubKey, HKEY * Result, bool CanCreate)
  383. {
  384. long R;
  385. DebugAssert(Configuration != NULL);
  386. DebugAssert(Key == HKEY_CURRENT_USER);
  387. DebugUsedParam(Key);
  388. UnicodeString RegKey = SubKey;
  389. int PuttyKeyLen = OriginalPuttyRegistryStorageKey.Length();
  390. DebugAssert(RegKey.SubString(1, PuttyKeyLen) == OriginalPuttyRegistryStorageKey);
  391. RegKey = RegKey.SubString(PuttyKeyLen + 1, RegKey.Length() - PuttyKeyLen);
  392. if (!RegKey.IsEmpty())
  393. {
  394. DebugAssert(RegKey[1] == L'\\');
  395. RegKey.Delete(1, 1);
  396. }
  397. if (RegKey.IsEmpty())
  398. {
  399. *Result = static_cast<HKEY>(NULL);
  400. R = ERROR_SUCCESS;
  401. }
  402. else
  403. {
  404. // we expect this to be called only from verify_host_key() or store_host_key()
  405. DebugAssert(RegKey == L"SshHostKeys");
  406. THierarchicalStorage * Storage = Configuration->CreateConfigStorage();
  407. Storage->AccessMode = (CanCreate ? smReadWrite : smRead);
  408. if (Storage->OpenSubKey(RegKey, CanCreate))
  409. {
  410. *Result = reinterpret_cast<HKEY>(Storage);
  411. R = ERROR_SUCCESS;
  412. }
  413. else
  414. {
  415. delete Storage;
  416. R = ERROR_CANTOPEN;
  417. }
  418. }
  419. return R;
  420. }
  421. //---------------------------------------------------------------------------
  422. long reg_open_winscp_key(HKEY Key, const char * SubKey, HKEY * Result)
  423. {
  424. return OpenWinSCPKey(Key, SubKey, Result, false);
  425. }
  426. //---------------------------------------------------------------------------
  427. long reg_create_winscp_key(HKEY Key, const char * SubKey, HKEY * Result)
  428. {
  429. return OpenWinSCPKey(Key, SubKey, Result, true);
  430. }
  431. //---------------------------------------------------------------------------
  432. long reg_query_winscp_value_ex(HKEY Key, const char * ValueName, unsigned long * /*Reserved*/,
  433. unsigned long * Type, unsigned char * Data, unsigned long * DataSize)
  434. {
  435. long R;
  436. DebugAssert(Configuration != NULL);
  437. THierarchicalStorage * Storage = reinterpret_cast<THierarchicalStorage *>(Key);
  438. AnsiString Value;
  439. if (Storage == NULL)
  440. {
  441. if (UnicodeString(ValueName) == L"RandSeedFile")
  442. {
  443. Value = AnsiString(Configuration->RandomSeedFileName);
  444. R = ERROR_SUCCESS;
  445. }
  446. else
  447. {
  448. DebugFail();
  449. R = ERROR_READ_FAULT;
  450. }
  451. }
  452. else
  453. {
  454. if (Storage->ValueExists(ValueName))
  455. {
  456. Value = AnsiString(Storage->ReadStringRaw(ValueName, L""));
  457. R = ERROR_SUCCESS;
  458. }
  459. else
  460. {
  461. R = ERROR_READ_FAULT;
  462. }
  463. }
  464. if (R == ERROR_SUCCESS)
  465. {
  466. DebugAssert(Type != NULL);
  467. *Type = REG_SZ;
  468. char * DataStr = reinterpret_cast<char *>(Data);
  469. strncpy(DataStr, Value.c_str(), *DataSize);
  470. DataStr[*DataSize - 1] = '\0';
  471. *DataSize = strlen(DataStr);
  472. }
  473. return R;
  474. }
  475. //---------------------------------------------------------------------------
  476. long reg_set_winscp_value_ex(HKEY Key, const char * ValueName, unsigned long /*Reserved*/,
  477. unsigned long Type, const unsigned char * Data, unsigned long DataSize)
  478. {
  479. DebugAssert(Configuration != NULL);
  480. DebugAssert(Type == REG_SZ);
  481. DebugUsedParam(Type);
  482. THierarchicalStorage * Storage = reinterpret_cast<THierarchicalStorage *>(Key);
  483. DebugAssert(Storage != NULL);
  484. if (Storage != NULL)
  485. {
  486. UnicodeString Value(reinterpret_cast<const char*>(Data), DataSize - 1);
  487. Storage->WriteStringRaw(ValueName, Value);
  488. }
  489. return ERROR_SUCCESS;
  490. }
  491. //---------------------------------------------------------------------------
  492. long reg_close_winscp_key(HKEY Key)
  493. {
  494. DebugAssert(Configuration != NULL);
  495. THierarchicalStorage * Storage = reinterpret_cast<THierarchicalStorage *>(Key);
  496. if (Storage != NULL)
  497. {
  498. delete Storage;
  499. }
  500. return ERROR_SUCCESS;
  501. }
  502. //---------------------------------------------------------------------------
  503. TKeyType KeyType(UnicodeString FileName)
  504. {
  505. DebugAssert(ktUnopenable == SSH_KEYTYPE_UNOPENABLE);
  506. DebugAssert(ktSSHCom == SSH_KEYTYPE_SSHCOM);
  507. DebugAssert(ktSSH2PublicOpenSSH == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH);
  508. UTF8String UtfFileName = UTF8String(FileName);
  509. Filename * KeyFile = filename_from_str(UtfFileName.c_str());
  510. TKeyType Result = (TKeyType)key_type(KeyFile);
  511. filename_free(KeyFile);
  512. return Result;
  513. }
  514. //---------------------------------------------------------------------------
  515. bool IsKeyEncrypted(TKeyType KeyType, const UnicodeString & FileName, UnicodeString & Comment)
  516. {
  517. UTF8String UtfFileName = UTF8String(FileName);
  518. Filename * KeyFile = filename_from_str(UtfFileName.c_str());
  519. bool Result;
  520. char * CommentStr = NULL;
  521. switch (KeyType)
  522. {
  523. case ktSSH2:
  524. Result = (ssh2_userkey_encrypted(KeyFile, &CommentStr) != 0);
  525. break;
  526. case ktOpenSSHPEM:
  527. case ktOpenSSHNew:
  528. case ktSSHCom:
  529. Result = (import_encrypted(KeyFile, KeyType, &CommentStr) != NULL);
  530. break;
  531. default:
  532. DebugFail();
  533. Result = false;
  534. break;
  535. }
  536. if (CommentStr != NULL)
  537. {
  538. Comment = UnicodeString(AnsiString(CommentStr));
  539. // ktOpenSSH has no comment, PuTTY defaults to file path
  540. if (Comment == FileName)
  541. {
  542. Comment = ExtractFileName(FileName);
  543. }
  544. sfree(CommentStr);
  545. }
  546. return Result;
  547. }
  548. //---------------------------------------------------------------------------
  549. TPrivateKey * LoadKey(TKeyType KeyType, const UnicodeString & FileName, const UnicodeString & Passphrase)
  550. {
  551. UTF8String UtfFileName = UTF8String(FileName);
  552. Filename * KeyFile = filename_from_str(UtfFileName.c_str());
  553. AnsiString AnsiPassphrase = Passphrase;
  554. struct ssh2_userkey * Ssh2Key = NULL;
  555. const char * ErrorStr = NULL;
  556. switch (KeyType)
  557. {
  558. case ktSSH2:
  559. Ssh2Key = ssh2_load_userkey(KeyFile, AnsiPassphrase.c_str(), &ErrorStr);
  560. break;
  561. case ktOpenSSHPEM:
  562. case ktOpenSSHNew:
  563. case ktSSHCom:
  564. Ssh2Key = import_ssh2(KeyFile, KeyType, AnsiPassphrase.c_str(), &ErrorStr);
  565. break;
  566. default:
  567. DebugFail();
  568. break;
  569. }
  570. Shred(AnsiPassphrase);
  571. if (Ssh2Key == NULL)
  572. {
  573. UnicodeString Error = AnsiString(ErrorStr);
  574. // While theoretically we may get "unable to open key file" and
  575. // so we should check system error code,
  576. // we actully never get here unless we call KeyType previously
  577. // and handle ktUnopenable accordingly.
  578. throw Exception(Error);
  579. }
  580. else if (Ssh2Key == SSH2_WRONG_PASSPHRASE)
  581. {
  582. throw Exception(LoadStr(AUTH_TRANSL_WRONG_PASSPHRASE));
  583. }
  584. return reinterpret_cast<TPrivateKey *>(Ssh2Key);
  585. }
  586. //---------------------------------------------------------------------------
  587. void ChangeKeyComment(TPrivateKey * PrivateKey, const UnicodeString & Comment)
  588. {
  589. AnsiString AnsiComment(Comment);
  590. struct ssh2_userkey * Ssh2Key = reinterpret_cast<struct ssh2_userkey *>(PrivateKey);
  591. sfree(Ssh2Key->comment);
  592. Ssh2Key->comment = dupstr(AnsiComment.c_str());
  593. }
  594. //---------------------------------------------------------------------------
  595. void SaveKey(TKeyType KeyType, const UnicodeString & FileName,
  596. const UnicodeString & Passphrase, TPrivateKey * PrivateKey)
  597. {
  598. UTF8String UtfFileName = UTF8String(FileName);
  599. Filename * KeyFile = filename_from_str(UtfFileName.c_str());
  600. struct ssh2_userkey * Ssh2Key = reinterpret_cast<struct ssh2_userkey *>(PrivateKey);
  601. AnsiString AnsiPassphrase = Passphrase;
  602. char * PassphrasePtr = (AnsiPassphrase.IsEmpty() ? NULL : AnsiPassphrase.c_str());
  603. switch (KeyType)
  604. {
  605. case ktSSH2:
  606. if (!ssh2_save_userkey(KeyFile, Ssh2Key, PassphrasePtr))
  607. {
  608. int Error = errno;
  609. throw EOSExtException(FMTLOAD(KEY_SAVE_ERROR, (FileName)), Error);
  610. }
  611. break;
  612. default:
  613. DebugFail();
  614. break;
  615. }
  616. }
  617. //---------------------------------------------------------------------------
  618. void FreeKey(TPrivateKey * PrivateKey)
  619. {
  620. struct ssh2_userkey * Ssh2Key = reinterpret_cast<struct ssh2_userkey *>(PrivateKey);
  621. Ssh2Key->alg->freekey(Ssh2Key->data);
  622. sfree(Ssh2Key);
  623. }
  624. //---------------------------------------------------------------------------
  625. bool __fastcall HasGSSAPI(UnicodeString CustomPath)
  626. {
  627. static int has = -1;
  628. if (has < 0)
  629. {
  630. Conf * conf = conf_new();
  631. ssh_gss_liblist * List = NULL;
  632. try
  633. {
  634. Filename * filename = filename_from_str(UTF8String(CustomPath).c_str());
  635. conf_set_filename(conf, CONF_ssh_gss_custom, filename);
  636. filename_free(filename);
  637. List = ssh_gss_setup(conf);
  638. for (int Index = 0; (has <= 0) && (Index < List->nlibraries); Index++)
  639. {
  640. ssh_gss_library * library = &List->libraries[Index];
  641. Ssh_gss_ctx ctx;
  642. memset(&ctx, 0, sizeof(ctx));
  643. has =
  644. ((library->acquire_cred(library, &ctx) == SSH_GSS_OK) &&
  645. (library->release_cred(library, &ctx) == SSH_GSS_OK)) ? 1 : 0;
  646. }
  647. }
  648. __finally
  649. {
  650. ssh_gss_cleanup(List);
  651. conf_free(conf);
  652. }
  653. if (has < 0)
  654. {
  655. has = 0;
  656. }
  657. }
  658. return (has > 0);
  659. }
  660. //---------------------------------------------------------------------------
  661. static void __fastcall DoNormalizeFingerprint(UnicodeString & Fingerprint, UnicodeString & KeyType)
  662. {
  663. const wchar_t NormalizedSeparator = L'-';
  664. const int MaxCount = 10;
  665. const ssh_signkey * SignKeys[MaxCount];
  666. int Count = LENOF(SignKeys);
  667. // We may use find_pubkey_alg, but it gets complicated with normalized fingerprint
  668. // as the names have different number of dashes
  669. get_hostkey_algs(&Count, SignKeys);
  670. for (int Index = 0; Index < Count; Index++)
  671. {
  672. const ssh_signkey * SignKey = SignKeys[Index];
  673. UnicodeString Name = UnicodeString(SignKey->name);
  674. if (StartsStr(Name + L" ", Fingerprint))
  675. {
  676. int LenStart = Name.Length() + 1;
  677. Fingerprint[LenStart] = NormalizedSeparator;
  678. int Space = Fingerprint.Pos(L" ");
  679. // If not a number, it's an invalid input,
  680. // either something completelly wrong, or it can be OpenSSH base64 public key,
  681. // that got here from TPasteKeyHandler::Paste
  682. if (IsNumber(Fingerprint.SubString(LenStart + 1, Space - LenStart - 1)))
  683. {
  684. Fingerprint.Delete(LenStart + 1, Space - LenStart);
  685. Fingerprint = ReplaceChar(Fingerprint, L':', NormalizedSeparator);
  686. KeyType = UnicodeString(SignKey->keytype);
  687. return;
  688. }
  689. }
  690. else if (StartsStr(Name + NormalizedSeparator, Fingerprint))
  691. {
  692. KeyType = UnicodeString(SignKey->keytype);
  693. return;
  694. }
  695. }
  696. }
  697. //---------------------------------------------------------------------------
  698. UnicodeString __fastcall NormalizeFingerprint(UnicodeString Fingerprint)
  699. {
  700. UnicodeString KeyType; // unused
  701. DoNormalizeFingerprint(Fingerprint, KeyType);
  702. return Fingerprint;
  703. }
  704. //---------------------------------------------------------------------------
  705. UnicodeString __fastcall KeyTypeFromFingerprint(UnicodeString Fingerprint)
  706. {
  707. UnicodeString KeyType;
  708. DoNormalizeFingerprint(Fingerprint, KeyType);
  709. return KeyType;
  710. }
  711. //---------------------------------------------------------------------------
  712. UnicodeString __fastcall GetPuTTYVersion()
  713. {
  714. // "Release 0.64"
  715. // "Pre-release 0.65:2015-07-20.95501a1"
  716. // "Development snapshot 2015-12-22.51465fa"
  717. UnicodeString Result = get_putty_version();
  718. // Skip "Release", "Pre-release", "Development snapshot"
  719. int P = Result.LastDelimiter(L" ");
  720. Result.Delete(1, P);
  721. return Result;
  722. }
  723. //---------------------------------------------------------------------------
  724. UnicodeString __fastcall Sha256(const char * Data, size_t Size)
  725. {
  726. unsigned char Digest[32];
  727. SHA256_Simple(Data, Size, Digest);
  728. UnicodeString Result(BytesToHex(Digest, LENOF(Digest)));
  729. return Result;
  730. }
  731. //---------------------------------------------------------------------------
  732. void __fastcall DllHijackingProtection()
  733. {
  734. dll_hijacking_protection();
  735. }
  736. //---------------------------------------------------------------------------
  737. UnicodeString __fastcall ParseOpenSshPubLine(const UnicodeString & Line, const struct ssh_signkey *& Algorithm)
  738. {
  739. UTF8String UtfLine = UTF8String(Line);
  740. char * AlgorithmName = NULL;
  741. int PubBlobLen = 0;
  742. char * CommentPtr = NULL;
  743. const char * ErrorStr = NULL;
  744. unsigned char * PubBlob = openssh_loadpub_line(UtfLine.c_str(), &AlgorithmName, &PubBlobLen, &CommentPtr, &ErrorStr);
  745. UnicodeString Result;
  746. if (PubBlob == NULL)
  747. {
  748. throw Exception(UnicodeString(ErrorStr));
  749. }
  750. else
  751. {
  752. try
  753. {
  754. Algorithm = find_pubkey_alg(AlgorithmName);
  755. if (Algorithm == NULL)
  756. {
  757. throw Exception(FORMAT(L"Unknown public key algorithm \"%s\".", (AlgorithmName)));
  758. }
  759. void * Key = Algorithm->newkey(Algorithm, reinterpret_cast<const char*>(PubBlob), PubBlobLen);
  760. if (Key == NULL)
  761. {
  762. throw Exception(L"Invalid public key.");
  763. }
  764. char * FmtKey = Algorithm->fmtkey(Key);
  765. Result = UnicodeString(FmtKey);
  766. sfree(FmtKey);
  767. Algorithm->freekey(Key);
  768. }
  769. __finally
  770. {
  771. sfree(PubBlob);
  772. sfree(AlgorithmName);
  773. sfree(CommentPtr);
  774. }
  775. }
  776. return Result;
  777. }
  778. //---------------------------------------------------------------------------
  779. //---------------------------------------------------------------------------