PuttyIntf.cpp 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616
  1. //---------------------------------------------------------------------------
  2. #include <CorePCH.h>
  3. #pragma hdrstop
  4. #include "PuttyIntf.h"
  5. #include "SecureShell.h"
  6. #include "Exceptions.h"
  7. #include <Soap.EncdDecd.hpp>
  8. //---------------------------------------------------------------------------
  9. char sshver[50];
  10. extern const char commitid[] = "";
  11. const bool platform_uses_x11_unix_by_default = true;
  12. CRITICAL_SECTION putty_section;
  13. static bool SaveRandomSeed;
  14. static bool HadRandomSeed;
  15. static char appname_[50];
  16. const char *const appname = appname_;
  17. extern const bool share_can_be_downstream = false;
  18. extern const bool share_can_be_upstream = false;
  19. //---------------------------------------------------------------------------
  20. extern "C"
  21. {
  22. #include <windows\platform.h>
  23. }
  24. const UnicodeString OriginalPuttyRegistryStorageKey(_T(PUTTY_REG_POS));
  25. const UnicodeString KittyRegistryStorageKey(L"Software\\9bis.com\\KiTTY");
  26. const UnicodeString OriginalPuttyExecutable("putty.exe");
  27. const UnicodeString KittyExecutable("kitty.exe");
  28. const UnicodeString PuttyKeyExt(L"ppk");
  29. //---------------------------------------------------------------------------
  30. void __fastcall PuttyInitialize()
  31. {
  32. SaveRandomSeed = true;
  33. InitializeCriticalSection(&putty_section);
  34. HadRandomSeed = FileExists(ApiPath(Configuration->RandomSeedFileName));
  35. if (HadRandomSeed)
  36. {
  37. AppLog(L"Random seed file exists");
  38. }
  39. // make sure random generator is initialised, so random_save_seed()
  40. // in destructor can proceed
  41. random_ref();
  42. sk_init();
  43. AnsiString VersionString = AnsiString(SshVersionString());
  44. DebugAssert(!VersionString.IsEmpty() && (static_cast<size_t>(VersionString.Length()) < LENOF(sshver)));
  45. strcpy(sshver, VersionString.c_str());
  46. AnsiString AppName = AnsiString(AppNameString());
  47. DebugAssert(!AppName.IsEmpty() && (static_cast<size_t>(AppName.Length()) < LENOF(appname_)));
  48. strcpy(appname_, AppName.c_str());
  49. }
  50. //---------------------------------------------------------------------------
  51. static bool DeleteRandomSeedOnExit()
  52. {
  53. return !HadRandomSeed && !SaveRandomSeed;
  54. }
  55. //---------------------------------------------------------------------------
  56. void __fastcall PuttyFinalize()
  57. {
  58. if (SaveRandomSeed)
  59. {
  60. AppLog(L"Saving random seed file");
  61. random_save_seed();
  62. }
  63. random_unref();
  64. // random_ref in PuttyInitialize creates the seed file. Delete it, if we didn't want to create it.
  65. if (DeleteRandomSeedOnExit())
  66. {
  67. AppLog(L"Deleting unwanted random seed file");
  68. DeleteFile(ApiPath(Configuration->RandomSeedFileName));
  69. }
  70. sk_cleanup();
  71. win_misc_cleanup();
  72. win_secur_cleanup();
  73. ec_cleanup();
  74. wingss_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 * GetSeatSecureShell(Seat * seat)
  91. {
  92. DebugAssert(seat != NULL);
  93. if (is_tempseat(seat))
  94. {
  95. seat = tempseat_get_real(seat);
  96. }
  97. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  98. return SecureShell;
  99. }
  100. //---------------------------------------------------------------------------
  101. TSecureShell * GetSecureShell(Plug * plug, bool & pfwd)
  102. {
  103. if (!is_ssh(plug) && !is_pfwd(plug))
  104. {
  105. // If it is not SSH/PFwd plug, then it must be Proxy plug.
  106. // Get SSH/PFwd plug which it wraps.
  107. ProxySocket * AProxySocket = get_proxy_plug_socket(plug);
  108. plug = AProxySocket->plug;
  109. }
  110. pfwd = is_pfwd(plug);
  111. Seat * seat;
  112. if (pfwd)
  113. {
  114. seat = get_pfwd_seat(plug);
  115. }
  116. else
  117. {
  118. seat = get_ssh_seat(plug);
  119. }
  120. DebugAssert(seat != NULL);
  121. return GetSeatSecureShell(seat);
  122. }
  123. //---------------------------------------------------------------------------
  124. struct callback_set * get_callback_set(Plug * plug)
  125. {
  126. bool pfwd;
  127. TSecureShell * SecureShell = GetSecureShell(plug, pfwd);
  128. return SecureShell->GetCallbackSet();
  129. }
  130. //---------------------------------------------------------------------------
  131. struct callback_set * get_seat_callback_set(Seat * seat)
  132. {
  133. TSecureShell * SecureShell = GetSeatSecureShell(seat);
  134. return SecureShell->GetCallbackSet();
  135. }
  136. //---------------------------------------------------------------------------
  137. extern "C" const char * do_select(Plug * plug, SOCKET skt, bool enable)
  138. {
  139. bool pfwd;
  140. TSecureShell * SecureShell = GetSecureShell(plug, pfwd);
  141. if (!pfwd)
  142. {
  143. SecureShell->UpdateSocket(skt, enable);
  144. }
  145. else
  146. {
  147. SecureShell->UpdatePortFwdSocket(skt, enable);
  148. }
  149. return NULL;
  150. }
  151. //---------------------------------------------------------------------------
  152. static size_t output(Seat * seat, SeatOutputType type, const void * data, size_t len)
  153. {
  154. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  155. if (static_cast<int>(static_cast<char>(type)) == -1)
  156. {
  157. SecureShell->CWrite(reinterpret_cast<const char *>(data), len);
  158. }
  159. else if (type != SEAT_OUTPUT_STDERR)
  160. {
  161. SecureShell->FromBackend(reinterpret_cast<const unsigned char *>(data), len);
  162. }
  163. else
  164. {
  165. SecureShell->AddStdError(reinterpret_cast<const char *>(data), len);
  166. }
  167. return 0;
  168. }
  169. //---------------------------------------------------------------------------
  170. static bool eof(Seat *)
  171. {
  172. return false;
  173. }
  174. //---------------------------------------------------------------------------
  175. static SeatPromptResult get_userpass_input(Seat * seat, prompts_t * p)
  176. {
  177. DebugAssert(p != NULL);
  178. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  179. DebugAssert(SecureShell != NULL);
  180. SeatPromptResult Result;
  181. TStrings * Prompts = new TStringList();
  182. TStrings * Results = new TStringList();
  183. try
  184. {
  185. UnicodeString Name = UTF8ToString(p->name);
  186. for (int Index = 0; Index < int(p->n_prompts); Index++)
  187. {
  188. prompt_t * Prompt = p->prompts[Index];
  189. UnicodeString S;
  190. if (p->utf8)
  191. {
  192. S = UTF8ToString(Prompt->prompt);
  193. }
  194. else
  195. {
  196. S = UnicodeString(AnsiString(Prompt->prompt));
  197. }
  198. Prompts->AddObject(S, (TObject *)(FLAGMASK(Prompt->echo, pupEcho)));
  199. // this fails, when new passwords do not match on change password prompt,
  200. // and putty retries the prompt
  201. DebugAssert(strlen(prompt_get_result_ref(Prompt)) == 0);
  202. Results->Add(L"");
  203. }
  204. UnicodeString Instructions = UTF8ToString(p->instruction);
  205. if (SecureShell->PromptUser(p->to_server, Name, p->name_reqd,
  206. Instructions, p->instr_reqd, Prompts, Results))
  207. {
  208. for (int Index = 0; Index < int(p->n_prompts); Index++)
  209. {
  210. prompt_t * Prompt = p->prompts[Index];
  211. RawByteString S;
  212. if (p->utf8)
  213. {
  214. S = RawByteString(UTF8String(Results->Strings[Index]));
  215. }
  216. else
  217. {
  218. S = RawByteString(AnsiString(Results->Strings[Index]));
  219. }
  220. prompt_set_result(Prompt, S.c_str());
  221. }
  222. Result = SPR_OK;
  223. }
  224. else
  225. {
  226. Result = SPR_USER_ABORT;
  227. }
  228. }
  229. __finally
  230. {
  231. delete Prompts;
  232. delete Results;
  233. }
  234. return Result;
  235. }
  236. //---------------------------------------------------------------------------
  237. static void connection_fatal(Seat * seat, const char * message)
  238. {
  239. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  240. SecureShell->PuttyFatalError(UnicodeString(AnsiString(message)));
  241. }
  242. //---------------------------------------------------------------------------
  243. static void nonfatal(Seat *, const char * message)
  244. {
  245. // there's no place in our putty code, where this is called
  246. DebugFail();
  247. AppLog(UnicodeString(AnsiString(message)));
  248. }
  249. //---------------------------------------------------------------------------
  250. SeatPromptResult confirm_ssh_host_key(Seat * seat, const char * host, int port, const char * keytype,
  251. char * keystr, SeatDialogText *, HelpCtx,
  252. void (*DebugUsedArg(callback))(void *ctx, SeatPromptResult result), void * DebugUsedArg(ctx),
  253. char **key_fingerprints, bool is_certificate, int ca_count, bool already_verified)
  254. {
  255. UnicodeString FingerprintSHA256, FingerprintMD5;
  256. if (key_fingerprints[SSH_FPTYPE_SHA256] != NULL)
  257. {
  258. FingerprintSHA256 = key_fingerprints[SSH_FPTYPE_SHA256];
  259. }
  260. if (DebugAlwaysTrue(key_fingerprints[SSH_FPTYPE_MD5] != NULL))
  261. {
  262. FingerprintMD5 = key_fingerprints[SSH_FPTYPE_MD5];
  263. }
  264. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  265. SecureShell->VerifyHostKey(
  266. host, port, keytype, keystr, FingerprintSHA256, FingerprintMD5, is_certificate, ca_count, already_verified);
  267. // We should return 0 when key was not confirmed, we throw exception instead.
  268. return SPR_OK;
  269. }
  270. //---------------------------------------------------------------------------
  271. bool have_ssh_host_key(Seat * seat, const char * hostname, int port,
  272. const char * keytype)
  273. {
  274. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  275. return SecureShell->HaveHostKey(hostname, port, keytype) ? 1 : 0;
  276. }
  277. //---------------------------------------------------------------------------
  278. SeatPromptResult confirm_weak_crypto_primitive(
  279. Seat * seat, SeatDialogText *,
  280. void (*DebugUsedArg(callback))(void * ctx, SeatPromptResult result), void * DebugUsedArg(ctx),
  281. const char * algtype, const char *algname, int wcr)
  282. {
  283. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  284. SecureShell->AskAlg(algtype, algname, wcr);
  285. // We should return 0 when alg was not confirmed, we throw exception instead.
  286. return SPR_OK;
  287. }
  288. //---------------------------------------------------------------------------
  289. SeatPromptResult confirm_weak_cached_hostkey(
  290. Seat *, SeatDialogText *,
  291. void (*DebugUsedArg(callback))(void * ctx, SeatPromptResult result), void * DebugUsedArg(ctx))
  292. {
  293. return SPR_OK;
  294. }
  295. //---------------------------------------------------------------------------
  296. const SeatDialogPromptDescriptions * prompt_descriptions(Seat *)
  297. {
  298. static const SeatDialogPromptDescriptions descs = {
  299. /*.hk_accept_action =*/ "",
  300. /*.hk_connect_once_action =*/ "",
  301. /*.hk_cancel_action =*/ "",
  302. /*.hk_cancel_action_Participle =*/ "",
  303. NULL, NULL,
  304. };
  305. return &descs;
  306. }
  307. //---------------------------------------------------------------------------
  308. void old_keyfile_warning(void)
  309. {
  310. // no reference to TSecureShell instance available - and we already warn on Login dialog
  311. }
  312. //---------------------------------------------------------------------------
  313. size_t banner(Seat * seat, const void * data, size_t len)
  314. {
  315. TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
  316. UnicodeString Banner(UTF8String(static_cast<const char *>(data), len));
  317. SecureShell->DisplayBanner(Banner);
  318. return 0; // PuTTY never uses the value
  319. }
  320. //---------------------------------------------------------------------------
  321. NORETURN static void SSHFatalError(const char * Format, va_list Param)
  322. {
  323. char Buf[200];
  324. vsnprintf(Buf, LENOF(Buf), Format, Param);
  325. Buf[LENOF(Buf) - 1] = '\0';
  326. // Only few calls from putty\winnet.c might be connected with specific
  327. // TSecureShell. Otherwise called only for really fatal errors
  328. // like 'out of memory' from putty\ssh.c.
  329. throw ESshFatal(NULL, Buf);
  330. }
  331. //---------------------------------------------------------------------------
  332. void modalfatalbox(const char * fmt, ...)
  333. {
  334. va_list Param;
  335. va_start(Param, fmt);
  336. SSHFatalError(fmt, Param);
  337. va_end(Param);
  338. }
  339. //---------------------------------------------------------------------------
  340. void nonfatal(const char * fmt, ...)
  341. {
  342. va_list Param;
  343. va_start(Param, fmt);
  344. SSHFatalError(fmt, Param);
  345. va_end(Param);
  346. }
  347. //---------------------------------------------------------------------------
  348. void ldisc_echoedit_update(Ldisc * /*handle*/)
  349. {
  350. DebugFail();
  351. }
  352. //---------------------------------------------------------------------------
  353. unsigned long schedule_timer(int ticks, timer_fn_t /*fn*/, void * /*ctx*/)
  354. {
  355. return ticks + GetTickCount();
  356. }
  357. //---------------------------------------------------------------------------
  358. void expire_timer_context(void * /*ctx*/)
  359. {
  360. // nothing
  361. }
  362. //---------------------------------------------------------------------------
  363. Pinger * pinger_new(Conf * /*conf*/, Backend * /*back*/)
  364. {
  365. return NULL;
  366. }
  367. //---------------------------------------------------------------------------
  368. void pinger_reconfig(Pinger * /*pinger*/, Conf * /*oldconf*/, Conf * /*newconf*/)
  369. {
  370. // nothing
  371. }
  372. //---------------------------------------------------------------------------
  373. void pinger_free(Pinger * /*pinger*/)
  374. {
  375. // nothing
  376. }
  377. //---------------------------------------------------------------------------
  378. void platform_get_x11_auth(struct X11Display * /*display*/, Conf * /*conf*/)
  379. {
  380. // nothing, therefore no auth.
  381. }
  382. //---------------------------------------------------------------------------
  383. // Based on PuTTY's settings.c
  384. char * get_remote_username(Conf * conf)
  385. {
  386. char * username = conf_get_str(conf, CONF_username);
  387. char * result;
  388. if (*username)
  389. {
  390. result = dupstr(username);
  391. }
  392. else
  393. {
  394. result = NULL;
  395. }
  396. return result;
  397. }
  398. //---------------------------------------------------------------------------
  399. static const SeatVtable ScpSeatVtable =
  400. {
  401. output,
  402. eof,
  403. nullseat_sent,
  404. banner,
  405. get_userpass_input,
  406. nullseat_notify_session_started,
  407. nullseat_notify_remote_exit,
  408. nullseat_notify_remote_disconnect,
  409. connection_fatal,
  410. nonfatal,
  411. nullseat_update_specials_menu,
  412. nullseat_get_ttymode,
  413. nullseat_set_busy_status,
  414. confirm_ssh_host_key,
  415. confirm_weak_crypto_primitive,
  416. confirm_weak_cached_hostkey,
  417. prompt_descriptions,
  418. nullseat_is_always_utf8,
  419. nullseat_echoedit_update,
  420. nullseat_get_x_display,
  421. nullseat_get_windowid,
  422. nullseat_get_window_pixel_size,
  423. nullseat_stripctrl_new,
  424. nullseat_set_trust_status,
  425. nullseat_can_set_trust_status_yes,
  426. nullseat_has_mixed_input_stream_yes,
  427. nullseat_verbose_yes,
  428. nullseat_interactive_no,
  429. nullseat_get_cursor_position,
  430. };
  431. //---------------------------------------------------------------------------
  432. ScpSeat::ScpSeat(TSecureShell * ASecureShell)
  433. {
  434. SecureShell = ASecureShell;
  435. vt = &ScpSeatVtable;
  436. }
  437. //---------------------------------------------------------------------------
  438. std::unique_ptr<TCriticalSection> PuttyStorageSection(TraceInitPtr(new TCriticalSection()));
  439. THierarchicalStorage * PuttyStorage = NULL;
  440. enum TPuttyRegistryMode { prmPass, prmRedirect, prmCollect, prmFail };
  441. static TPuttyRegistryMode PuttyRegistryMode = prmRedirect;
  442. typedef std::map<UnicodeString, unsigned long> TPuttyRegistryTypes;
  443. static TPuttyRegistryTypes PuttyRegistryTypes;
  444. static HKEY RandSeedFileStorage = reinterpret_cast<HKEY>(1);
  445. //---------------------------------------------------------------------------
  446. int reg_override_winscp()
  447. {
  448. return (PuttyRegistryMode != prmPass);
  449. }
  450. //---------------------------------------------------------------------------
  451. void putty_registry_pass(bool enable)
  452. {
  453. if (enable)
  454. {
  455. PuttyStorageSection->Enter();
  456. DebugAssert(PuttyRegistryMode == prmRedirect);
  457. PuttyRegistryMode = prmPass;
  458. }
  459. else
  460. {
  461. DebugAssert(PuttyRegistryMode == prmPass);
  462. PuttyRegistryMode = prmRedirect;
  463. PuttyStorageSection->Leave();
  464. }
  465. }
  466. //---------------------------------------------------------------------------
  467. HKEY open_regkey_fn_winscp(bool Create, bool Write, HKEY Key, const char * Path, ...)
  468. {
  469. DebugUsedParam(Write);
  470. HKEY Result;
  471. if (PuttyRegistryMode == prmCollect)
  472. {
  473. // Note that for prmCollect even !Write mode is supported (behaving like prmFail) - needed for do_defaults
  474. Result = reinterpret_cast<HKEY>(1);
  475. }
  476. else if (PuttyRegistryMode == prmFail)
  477. {
  478. Result = reinterpret_cast<HKEY>(false);
  479. }
  480. else if (PuttyRegistryMode == prmRedirect)
  481. {
  482. DebugAssert(Key == HKEY_CURRENT_USER);
  483. DebugUsedParam(Key);
  484. UnicodeString SubKey;
  485. va_list ap;
  486. va_start(ap, Path);
  487. for (; Path; Path = va_arg(ap, const char *))
  488. {
  489. if (!SubKey.IsEmpty())
  490. {
  491. SubKey = IncludeTrailingBackslash(SubKey);
  492. }
  493. SubKey += UnicodeString(UTF8String(Path));
  494. }
  495. int PuttyKeyLen = OriginalPuttyRegistryStorageKey.Length();
  496. DebugAssert(SubKey.SubString(1, PuttyKeyLen) == OriginalPuttyRegistryStorageKey);
  497. UnicodeString RegKey = SubKey.SubString(PuttyKeyLen + 1, SubKey.Length() - PuttyKeyLen);
  498. if (!RegKey.IsEmpty())
  499. {
  500. DebugAssert(RegKey[1] == L'\\');
  501. RegKey.Delete(1, 1);
  502. }
  503. if (RegKey.IsEmpty())
  504. {
  505. // Called from access_random_seed()
  506. Result = RandSeedFileStorage;
  507. }
  508. else
  509. {
  510. // we expect this to be called only from retrieve_host_key() or store_host_key()
  511. DebugAssert(RegKey == L"SshHostKeys");
  512. DebugAssert(PuttyStorage != NULL);
  513. DebugAssert(PuttyStorage->AccessMode == (Write ? smReadWrite : smRead));
  514. if (PuttyStorage->OpenSubKey(RegKey, Create))
  515. {
  516. Result = reinterpret_cast<HKEY>(PuttyStorage);
  517. }
  518. else
  519. {
  520. Result = NULL;
  521. }
  522. }
  523. }
  524. else
  525. {
  526. DebugFail();
  527. Result = NULL;
  528. }
  529. return Result;
  530. }
  531. //---------------------------------------------------------------------------
  532. bool get_reg_dword_winscp(HKEY, const char * DebugUsedArg(Name), DWORD * DebugUsedArg(Out))
  533. {
  534. bool Result;
  535. if (PuttyRegistryMode == prmFail)
  536. {
  537. Result = false;
  538. }
  539. else if (PuttyRegistryMode == prmCollect)
  540. {
  541. Result = false;
  542. }
  543. else
  544. {
  545. DebugFail();
  546. Result = false;
  547. }
  548. return Result;
  549. }
  550. //---------------------------------------------------------------------------
  551. char * get_reg_sz_winscp(HKEY Key, const char * Name)
  552. {
  553. char * Result;
  554. if (PuttyRegistryMode == prmCollect)
  555. {
  556. Result = NULL;
  557. }
  558. else if (DebugAlwaysTrue(PuttyRegistryMode == prmRedirect))
  559. {
  560. DebugAssert(Configuration != NULL);
  561. UnicodeString ValueName = UTF8String(Name);
  562. bool Success;
  563. UnicodeString Value;
  564. if (Key == RandSeedFileStorage)
  565. {
  566. if (ValueName == L"RandSeedFile")
  567. {
  568. Value = Configuration->RandomSeedFileName;
  569. Success = true;
  570. }
  571. else
  572. {
  573. DebugFail();
  574. Success = false;
  575. }
  576. }
  577. else
  578. {
  579. THierarchicalStorage * Storage = reinterpret_cast<THierarchicalStorage *>(Key);
  580. if (Storage->ValueExists(ValueName))
  581. {
  582. Value = Storage->ReadStringRaw(ValueName, L"");
  583. Success = true;
  584. }
  585. else
  586. {
  587. Success = false;
  588. }
  589. }
  590. if (!Success)
  591. {
  592. Result = NULL;
  593. }
  594. else
  595. {
  596. AnsiString ValueAnsi = AnsiString(Value);
  597. Result = snewn(ValueAnsi.Length() + 1, char);
  598. strcpy(Result, ValueAnsi.c_str());
  599. }
  600. }
  601. else
  602. {
  603. Result = NULL;
  604. }
  605. return Result;
  606. }
  607. //---------------------------------------------------------------------------
  608. bool put_reg_dword_winscp(HKEY DebugUsedArg(Key), const char * Name, DWORD DebugUsedArg(Value))
  609. {
  610. bool Result;
  611. if (PuttyRegistryMode == prmCollect)
  612. {
  613. UnicodeString ValueName = UTF8String(Name);
  614. PuttyRegistryTypes[ValueName] = REG_DWORD;
  615. Result = true;
  616. }
  617. else if (PuttyRegistryMode == prmRedirect)
  618. {
  619. // Might need to implement this for CA
  620. DebugFail();
  621. Result = false;
  622. }
  623. else
  624. {
  625. DebugFail();
  626. return false;
  627. }
  628. return Result;
  629. }
  630. //---------------------------------------------------------------------------
  631. bool put_reg_sz_winscp(HKEY Key, const char * Name, const char * Str)
  632. {
  633. UnicodeString ValueName = UTF8String(Name);
  634. bool Result;
  635. if (PuttyRegistryMode == prmCollect)
  636. {
  637. PuttyRegistryTypes[ValueName] = REG_SZ;
  638. Result = true;
  639. }
  640. else if (PuttyRegistryMode == prmRedirect)
  641. {
  642. UnicodeString Value = UTF8String(Str);
  643. DebugAssert(Key != RandSeedFileStorage);
  644. THierarchicalStorage * Storage = reinterpret_cast<THierarchicalStorage *>(Key);
  645. DebugAssert(Storage != NULL);
  646. if (Storage != NULL)
  647. {
  648. Storage->WriteStringRaw(ValueName, Value);
  649. }
  650. Result = true;
  651. }
  652. else
  653. {
  654. DebugFail();
  655. Result = false;
  656. }
  657. return Result;
  658. }
  659. //---------------------------------------------------------------------------
  660. void close_regkey_winscp(HKEY)
  661. {
  662. DebugAssert((PuttyRegistryMode == prmCollect) || (PuttyRegistryMode == prmRedirect));
  663. }
  664. //---------------------------------------------------------------------------
  665. strbuf * get_reg_multi_sz_winscp(HKEY, const char * DebugUsedArg(name))
  666. {
  667. // Needed for CA
  668. DebugFail();
  669. return NULL;
  670. }
  671. //---------------------------------------------------------------------------
  672. TKeyType KeyType(UnicodeString FileName)
  673. {
  674. DebugAssert(ktUnopenable == SSH_KEYTYPE_UNOPENABLE);
  675. DebugAssert(ktSSHCom == SSH_KEYTYPE_SSHCOM);
  676. DebugAssert(ktSSH2PublicOpenSSH == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH);
  677. UTF8String UtfFileName = UTF8String(FileName);
  678. Filename * KeyFile = filename_from_utf8(UtfFileName.c_str());
  679. TKeyType Result = (TKeyType)key_type(KeyFile);
  680. filename_free(KeyFile);
  681. return Result;
  682. }
  683. //---------------------------------------------------------------------------
  684. bool IsKeyEncrypted(TKeyType KeyType, const UnicodeString & FileName, UnicodeString & Comment)
  685. {
  686. UTF8String UtfFileName = UTF8String(FileName);
  687. bool Result;
  688. char * CommentStr = NULL;
  689. Filename * KeyFile = filename_from_utf8(UtfFileName.c_str());
  690. try
  691. {
  692. switch (KeyType)
  693. {
  694. case ktSSH2:
  695. Result = (ppk_encrypted_f(KeyFile, &CommentStr) != 0);
  696. break;
  697. case ktOpenSSHPEM:
  698. case ktOpenSSHNew:
  699. case ktSSHCom:
  700. Result = (import_encrypted(KeyFile, KeyType, &CommentStr) != NULL);
  701. break;
  702. default:
  703. DebugFail();
  704. Result = false;
  705. break;
  706. }
  707. }
  708. __finally
  709. {
  710. filename_free(KeyFile);
  711. }
  712. if (CommentStr != NULL)
  713. {
  714. Comment = UnicodeString(AnsiString(CommentStr));
  715. // ktOpenSSH has no comment, PuTTY defaults to file path
  716. if (Comment == FileName)
  717. {
  718. Comment = ExtractFileName(FileName);
  719. }
  720. sfree(CommentStr);
  721. }
  722. return Result;
  723. }
  724. //---------------------------------------------------------------------------
  725. TPrivateKey * LoadKey(TKeyType KeyType, const UnicodeString & FileName, const UnicodeString & Passphrase, UnicodeString & Error)
  726. {
  727. UTF8String UtfFileName = UTF8String(FileName);
  728. Filename * KeyFile = filename_from_utf8(UtfFileName.c_str());
  729. struct ssh2_userkey * Ssh2Key = NULL;
  730. const char * ErrorStr = NULL;
  731. AnsiString AnsiPassphrase = Passphrase;
  732. try
  733. {
  734. switch (KeyType)
  735. {
  736. case ktSSH2:
  737. Ssh2Key = ppk_load_f(KeyFile, AnsiPassphrase.c_str(), &ErrorStr);
  738. break;
  739. case ktOpenSSHPEM:
  740. case ktOpenSSHNew:
  741. case ktSSHCom:
  742. Ssh2Key = import_ssh2(KeyFile, KeyType, AnsiPassphrase.c_str(), &ErrorStr);
  743. break;
  744. default:
  745. DebugFail();
  746. break;
  747. }
  748. }
  749. __finally
  750. {
  751. Shred(AnsiPassphrase);
  752. filename_free(KeyFile);
  753. }
  754. if (Ssh2Key == NULL)
  755. {
  756. // While theoretically we may get "unable to open key file" and
  757. // so we should check system error code,
  758. // we actually never get here unless we call KeyType previously
  759. // and handle ktUnopenable accordingly.
  760. Error = AnsiString(ErrorStr);
  761. }
  762. else if (Ssh2Key == SSH2_WRONG_PASSPHRASE)
  763. {
  764. Error = EmptyStr;
  765. Ssh2Key = NULL;
  766. }
  767. return reinterpret_cast<TPrivateKey *>(Ssh2Key);
  768. }
  769. //---------------------------------------------------------------------------
  770. TPrivateKey * LoadKey(TKeyType KeyType, const UnicodeString & FileName, const UnicodeString & Passphrase)
  771. {
  772. UnicodeString Error;
  773. TPrivateKey * Result = LoadKey(KeyType, FileName, Passphrase, Error);
  774. if (Result == NULL)
  775. {
  776. if (!Error.IsEmpty())
  777. {
  778. throw Exception(Error);
  779. }
  780. else
  781. {
  782. throw Exception(LoadStr(AUTH_TRANSL_WRONG_PASSPHRASE));
  783. }
  784. }
  785. return Result;
  786. }
  787. //---------------------------------------------------------------------------
  788. UnicodeString TestKey(TKeyType KeyType, const UnicodeString & FileName)
  789. {
  790. UnicodeString Result;
  791. TPrivateKey * Key = LoadKey(KeyType, FileName, EmptyStr, Result);
  792. if (Key != NULL)
  793. {
  794. FreeKey(Key);
  795. }
  796. return Result;
  797. }
  798. //---------------------------------------------------------------------------
  799. void ChangeKeyComment(TPrivateKey * PrivateKey, const UnicodeString & Comment)
  800. {
  801. AnsiString AnsiComment(Comment);
  802. struct ssh2_userkey * Ssh2Key = reinterpret_cast<struct ssh2_userkey *>(PrivateKey);
  803. sfree(Ssh2Key->comment);
  804. Ssh2Key->comment = dupstr(AnsiComment.c_str());
  805. }
  806. //---------------------------------------------------------------------------
  807. // Based on cmdgen.c
  808. void AddCertificateToKey(TPrivateKey * PrivateKey, const UnicodeString & CertificateFileName)
  809. {
  810. struct ssh2_userkey * Ssh2Key = reinterpret_cast<struct ssh2_userkey *>(PrivateKey);
  811. TKeyType Type = KeyType(CertificateFileName);
  812. int Error = errno;
  813. if ((Type != SSH_KEYTYPE_SSH2_PUBLIC_RFC4716) &&
  814. (Type != SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH))
  815. {
  816. if (Type == ktUnopenable)
  817. {
  818. throw EOSExtException(FMTLOAD(CERTIFICATE_UNOPENABLE, (CertificateFileName)), Error);
  819. }
  820. else
  821. {
  822. throw Exception(FMTLOAD(KEYGEN_NOT_PUBLIC, (CertificateFileName)));
  823. }
  824. }
  825. UTF8String UtfCertificateFileName = UTF8String(CertificateFileName);
  826. Filename * CertFilename = filename_from_utf8(UtfCertificateFileName.c_str());
  827. LoadedFile * CertLoadedFile;
  828. try
  829. {
  830. const char * ErrorStr = NULL;
  831. CertLoadedFile = lf_load_keyfile(CertFilename, &ErrorStr);
  832. if (CertLoadedFile == NULL)
  833. {
  834. // not capturing errno, as this in unlikely file access error, after we have passed KeyType above
  835. throw ExtException(FMTLOAD(CERTIFICATE_UNOPENABLE, (CertificateFileName)), Error);
  836. }
  837. }
  838. __finally
  839. {
  840. filename_free(CertFilename);
  841. }
  842. strbuf * Pub = strbuf_new();
  843. char * AlgorithmName = NULL;
  844. try
  845. {
  846. const char * ErrorStr = NULL;
  847. char * CommentStr = NULL;
  848. if (!ppk_loadpub_s(BinarySource_UPCAST(CertLoadedFile), &AlgorithmName,
  849. BinarySink_UPCAST(Pub), &CommentStr, &ErrorStr))
  850. {
  851. UnicodeString Error = AnsiString(ErrorStr);
  852. throw ExtException(FMTLOAD(CERTIFICATE_LOAD_ERROR, (CertificateFileName)), Error);
  853. }
  854. sfree(CommentStr);
  855. }
  856. __finally
  857. {
  858. lf_free(CertLoadedFile);
  859. }
  860. const ssh_keyalg * KeyAlg;
  861. try
  862. {
  863. KeyAlg = find_pubkey_alg(AlgorithmName);
  864. if (KeyAlg == NULL)
  865. {
  866. throw Exception(FMTLOAD(PUB_KEY_UNKNOWN, (AlgorithmName)));
  867. }
  868. // Check the two public keys match apart from certificates
  869. strbuf * OldBasePub = strbuf_new();
  870. ssh_key_public_blob(ssh_key_base_key(Ssh2Key->key), BinarySink_UPCAST(OldBasePub));
  871. ssh_key * NewPubKey = ssh_key_new_pub(KeyAlg, ptrlen_from_strbuf(Pub));
  872. strbuf * NewBasePub = strbuf_new();
  873. ssh_key_public_blob(ssh_key_base_key(NewPubKey), BinarySink_UPCAST(NewBasePub));
  874. ssh_key_free(NewPubKey);
  875. bool Match = ptrlen_eq_ptrlen(ptrlen_from_strbuf(OldBasePub), ptrlen_from_strbuf(NewBasePub));
  876. strbuf_free(OldBasePub);
  877. strbuf_free(NewBasePub);
  878. if (!Match)
  879. {
  880. throw Exception(FMTLOAD(CERTIFICATE_NOT_MATCH, (CertificateFileName)));
  881. }
  882. strbuf * Priv = strbuf_new_nm();
  883. ssh_key_private_blob(Ssh2Key->key, BinarySink_UPCAST(Priv));
  884. ssh_key * NewKey = ssh_key_new_priv(KeyAlg, ptrlen_from_strbuf(Pub), ptrlen_from_strbuf(Priv));
  885. strbuf_free(Priv);
  886. if (NewKey == NULL)
  887. {
  888. throw Exception(FMTLOAD(CERTIFICATE_CANNOT_COMBINE, (CertificateFileName)));
  889. }
  890. ssh_key_free(Ssh2Key->key);
  891. Ssh2Key->key = NewKey;
  892. }
  893. __finally
  894. {
  895. strbuf_free(Pub);
  896. sfree(AlgorithmName);
  897. }
  898. }
  899. //---------------------------------------------------------------------------
  900. void SaveKey(TKeyType KeyType, const UnicodeString & FileName,
  901. const UnicodeString & Passphrase, TPrivateKey * PrivateKey)
  902. {
  903. UTF8String UtfFileName = UTF8String(FileName);
  904. Filename * KeyFile = filename_from_utf8(UtfFileName.c_str());
  905. try
  906. {
  907. struct ssh2_userkey * Ssh2Key = reinterpret_cast<struct ssh2_userkey *>(PrivateKey);
  908. AnsiString AnsiPassphrase = Passphrase;
  909. char * PassphrasePtr = (AnsiPassphrase.IsEmpty() ? NULL : AnsiPassphrase.c_str());
  910. switch (KeyType)
  911. {
  912. case ktSSH2:
  913. {
  914. ppk_save_parameters Params = ppk_save_default_parameters;
  915. if (Configuration->KeyVersion != 0)
  916. {
  917. Params.fmt_version = Configuration->KeyVersion;
  918. }
  919. if (!ppk_save_f(KeyFile, Ssh2Key, PassphrasePtr, &Params))
  920. {
  921. int Error = errno;
  922. throw EOSExtException(FMTLOAD(KEY_SAVE_ERROR, (FileName)), Error);
  923. }
  924. }
  925. break;
  926. default:
  927. DebugFail();
  928. break;
  929. }
  930. }
  931. __finally
  932. {
  933. filename_free(KeyFile);
  934. }
  935. }
  936. //---------------------------------------------------------------------------
  937. void FreeKey(TPrivateKey * PrivateKey)
  938. {
  939. struct ssh2_userkey * Ssh2Key = reinterpret_cast<struct ssh2_userkey *>(PrivateKey);
  940. ssh_key_free(Ssh2Key->key);
  941. sfree(Ssh2Key->comment);
  942. sfree(Ssh2Key);
  943. }
  944. //---------------------------------------------------------------------------
  945. RawByteString StrBufToString(strbuf * StrBuf)
  946. {
  947. return RawByteString(reinterpret_cast<char *>(StrBuf->s), StrBuf->len);
  948. }
  949. //---------------------------------------------------------------------------
  950. RawByteString LoadPublicKey(
  951. const UnicodeString & FileName, UnicodeString & Algorithm, UnicodeString & Comment, bool & HasCertificate)
  952. {
  953. RawByteString Result;
  954. UTF8String UtfFileName = UTF8String(FileName);
  955. Filename * KeyFile = filename_from_utf8(UtfFileName.c_str());
  956. try
  957. {
  958. char * AlgorithmStr = NULL;
  959. char * CommentStr = NULL;
  960. const char * ErrorStr = NULL;
  961. strbuf * PublicKeyBuf = strbuf_new();
  962. if (!ppk_loadpub_f(KeyFile, &AlgorithmStr, BinarySink_UPCAST(PublicKeyBuf), &CommentStr, &ErrorStr))
  963. {
  964. UnicodeString Error = UnicodeString(AnsiString(ErrorStr));
  965. throw Exception(Error);
  966. }
  967. Algorithm = UnicodeString(AnsiString(AlgorithmStr));
  968. const ssh_keyalg * KeyAlg = find_pubkey_alg(AlgorithmStr);
  969. HasCertificate = (KeyAlg != NULL) && KeyAlg->is_certificate;
  970. sfree(AlgorithmStr);
  971. Comment = UnicodeString(AnsiString(CommentStr));
  972. sfree(CommentStr);
  973. Result = StrBufToString(PublicKeyBuf);
  974. strbuf_free(PublicKeyBuf);
  975. }
  976. __finally
  977. {
  978. filename_free(KeyFile);
  979. }
  980. return Result;
  981. }
  982. //---------------------------------------------------------------------------
  983. UnicodeString GetPublicKeyLine(const UnicodeString & FileName, UnicodeString & Comment, bool & HasCertificate)
  984. {
  985. UnicodeString Algorithm;
  986. RawByteString PublicKey = LoadPublicKey(FileName, Algorithm, Comment, HasCertificate);
  987. UnicodeString PublicKeyBase64 = EncodeBase64(PublicKey.c_str(), PublicKey.Length());
  988. PublicKeyBase64 = ReplaceStr(PublicKeyBase64, L"\r", L"");
  989. PublicKeyBase64 = ReplaceStr(PublicKeyBase64, L"\n", L"");
  990. UnicodeString Result = FORMAT(L"%s %s %s", (Algorithm, PublicKeyBase64, Comment));
  991. return Result;
  992. }
  993. //---------------------------------------------------------------------------
  994. bool __fastcall HasGSSAPI(UnicodeString CustomPath)
  995. {
  996. static int has = -1;
  997. if (has < 0)
  998. {
  999. Conf * conf = conf_new();
  1000. ssh_gss_liblist * List = NULL;
  1001. try
  1002. {
  1003. Filename * filename = filename_from_utf8(UTF8String(CustomPath).c_str());
  1004. conf_set_filename(conf, CONF_ssh_gss_custom, filename);
  1005. filename_free(filename);
  1006. List = ssh_gss_setup(conf, NULL);
  1007. for (int Index = 0; (has <= 0) && (Index < List->nlibraries); Index++)
  1008. {
  1009. ssh_gss_library * library = &List->libraries[Index];
  1010. Ssh_gss_ctx ctx;
  1011. memset(&ctx, 0, sizeof(ctx));
  1012. has =
  1013. ((library->acquire_cred(library, &ctx, NULL) == SSH_GSS_OK) &&
  1014. (library->release_cred(library, &ctx) == SSH_GSS_OK)) ? 1 : 0;
  1015. }
  1016. }
  1017. __finally
  1018. {
  1019. ssh_gss_cleanup(List);
  1020. conf_free(conf);
  1021. }
  1022. if (has < 0)
  1023. {
  1024. has = 0;
  1025. }
  1026. }
  1027. return (has > 0);
  1028. }
  1029. //---------------------------------------------------------------------------
  1030. static void __fastcall DoNormalizeFingerprint(UnicodeString & Fingerprint, UnicodeString & KeyName, UnicodeString & KeyType)
  1031. {
  1032. cp_ssh_keyalg * SignKeys;
  1033. int Count;
  1034. // We may use find_pubkey_alg, but it gets complicated with normalized fingerprint
  1035. // as the names have different number of dashes
  1036. get_hostkey_algs(-1, &Count, &SignKeys);
  1037. try
  1038. {
  1039. for (int Index = 0; Index < Count; Index++)
  1040. {
  1041. cp_ssh_keyalg SignKey = SignKeys[Index];
  1042. UnicodeString Name = UnicodeString(SignKey->ssh_id);
  1043. if (StartsStr(Name + L" ", Fingerprint))
  1044. {
  1045. UnicodeString Rest = Fingerprint.SubString(Name.Length() + 2, Fingerprint.Length() - Name.Length() - 1);
  1046. int Space = Rest.Pos(L" ");
  1047. // If not a number, it's an invalid input,
  1048. // either something completely wrong, or it can be OpenSSH base64 public key,
  1049. // that got here from TPasteKeyHandler::Paste
  1050. if (IsNumber(Rest.SubString(1, Space - 1)))
  1051. {
  1052. KeyName = Name;
  1053. Fingerprint = Rest.SubString(Space + 1, Fingerprint.Length() - Space);
  1054. Fingerprint = Base64ToUrlSafe(Fingerprint);
  1055. Fingerprint = MD5ToUrlSafe(Fingerprint);
  1056. KeyType = UnicodeString(SignKey->cache_id);
  1057. return;
  1058. }
  1059. }
  1060. else if (StartsStr(Name + NormalizedFingerprintSeparator, Fingerprint))
  1061. {
  1062. KeyType = UnicodeString(SignKey->cache_id);
  1063. KeyName = Name;
  1064. Fingerprint.Delete(1, Name.Length() + 1);
  1065. return;
  1066. }
  1067. }
  1068. }
  1069. __finally
  1070. {
  1071. sfree(SignKeys);
  1072. }
  1073. }
  1074. //---------------------------------------------------------------------------
  1075. void __fastcall NormalizeFingerprint(UnicodeString & Fingerprint, UnicodeString & KeyName)
  1076. {
  1077. UnicodeString KeyType;
  1078. DoNormalizeFingerprint(Fingerprint, KeyName, KeyType);
  1079. }
  1080. //---------------------------------------------------------------------------
  1081. UnicodeString __fastcall KeyTypeFromFingerprint(UnicodeString Fingerprint)
  1082. {
  1083. UnicodeString KeyType;
  1084. UnicodeString KeyName; // unused
  1085. DoNormalizeFingerprint(Fingerprint, KeyName, KeyType);
  1086. return KeyType;
  1087. }
  1088. //---------------------------------------------------------------------------
  1089. UnicodeString __fastcall GetPuTTYVersion()
  1090. {
  1091. // "Release 0.64"
  1092. // "Pre-release 0.65:2015-07-20.95501a1"
  1093. // "Development snapshot 2015-12-22.51465fa"
  1094. UnicodeString Result = get_putty_version();
  1095. // Skip "Release", "Pre-release", "Development snapshot"
  1096. int P = Result.LastDelimiter(L" ");
  1097. Result.Delete(1, P);
  1098. return Result;
  1099. }
  1100. //---------------------------------------------------------------------------
  1101. UnicodeString __fastcall Sha256(const char * Data, size_t Size)
  1102. {
  1103. unsigned char Digest[32];
  1104. hash_simple(&ssh_sha256, make_ptrlen(Data, Size), Digest);
  1105. UnicodeString Result(BytesToHex(Digest, LENOF(Digest)));
  1106. return Result;
  1107. }
  1108. //---------------------------------------------------------------------------
  1109. UnicodeString CalculateFileChecksum(TStream * Stream, const UnicodeString & Alg)
  1110. {
  1111. const ssh_hashalg * HashAlg;
  1112. if (SameIdent(Alg, Sha256ChecksumAlg))
  1113. {
  1114. HashAlg = &ssh_sha256;
  1115. }
  1116. else if (SameIdent(Alg, Sha1ChecksumAlg))
  1117. {
  1118. HashAlg = &ssh_sha1;
  1119. }
  1120. else if (SameIdent(Alg, Md5ChecksumAlg))
  1121. {
  1122. HashAlg = &ssh_md5;
  1123. }
  1124. else
  1125. {
  1126. throw Exception(FMTLOAD(UNKNOWN_CHECKSUM, (Alg)));
  1127. }
  1128. RawByteString Buf;
  1129. ssh_hash * Hash = ssh_hash_new(HashAlg);
  1130. try
  1131. {
  1132. const int BlockSize = 32 * 1024;
  1133. TFileBuffer Buffer;
  1134. DWORD Read;
  1135. do
  1136. {
  1137. Buffer.Reset();
  1138. Read = Buffer.LoadStream(Stream, BlockSize, false);
  1139. if (Read > 0)
  1140. {
  1141. put_datapl(Hash, make_ptrlen(Buffer.Data, Read));
  1142. }
  1143. }
  1144. while (Read > 0);
  1145. }
  1146. __finally
  1147. {
  1148. Buf.SetLength(ssh_hash_alg(Hash)->hlen);
  1149. ssh_hash_final(Hash, reinterpret_cast<unsigned char *>(Buf.c_str()));
  1150. }
  1151. UnicodeString Result = BytesToHex(Buf);
  1152. return Result;
  1153. }
  1154. //---------------------------------------------------------------------------
  1155. UnicodeString __fastcall ParseOpenSshPubLine(const UnicodeString & Line, const struct ssh_keyalg *& Algorithm)
  1156. {
  1157. UTF8String UtfLine = UTF8String(Line);
  1158. char * AlgorithmName = NULL;
  1159. char * CommentPtr = NULL;
  1160. const char * ErrorStr = NULL;
  1161. strbuf * PubBlobBuf = strbuf_new();
  1162. BinarySource Source[1];
  1163. BinarySource_BARE_INIT(Source, UtfLine.c_str(), UtfLine.Length());
  1164. UnicodeString Result;
  1165. try
  1166. {
  1167. if (!openssh_loadpub(Source, &AlgorithmName, BinarySink_UPCAST(PubBlobBuf), &CommentPtr, &ErrorStr))
  1168. {
  1169. throw Exception(UnicodeString(ErrorStr));
  1170. }
  1171. else
  1172. {
  1173. Algorithm = find_pubkey_alg(AlgorithmName);
  1174. if (Algorithm == NULL)
  1175. {
  1176. throw Exception(FMTLOAD(PUB_KEY_UNKNOWN, (AlgorithmName)));
  1177. }
  1178. ptrlen PtrLen = { PubBlobBuf->s, PubBlobBuf->len };
  1179. ssh_key * Key = Algorithm->new_pub(Algorithm, PtrLen);
  1180. if (Key == NULL)
  1181. {
  1182. throw Exception(L"Invalid public key.");
  1183. }
  1184. char * FmtKey = Algorithm->cache_str(Key);
  1185. Result = UnicodeString(FmtKey);
  1186. sfree(FmtKey);
  1187. Algorithm->freekey(Key);
  1188. }
  1189. }
  1190. __finally
  1191. {
  1192. strbuf_free(PubBlobBuf);
  1193. sfree(AlgorithmName);
  1194. sfree(CommentPtr);
  1195. }
  1196. return Result;
  1197. }
  1198. //---------------------------------------------------------------------------
  1199. // Based on ca_refresh_pubkey_info
  1200. void ParseCertificatePublicKey(const UnicodeString & Str, RawByteString & PublicKey, UnicodeString & Fingerprint)
  1201. {
  1202. AnsiString AnsiStr = AnsiString(Str);
  1203. ptrlen Data = ptrlen_from_asciz(AnsiStr.c_str());
  1204. strbuf * Blob = strbuf_new();
  1205. try
  1206. {
  1207. // See if we have a plain base64-encoded public key blob.
  1208. if (base64_valid(Data))
  1209. {
  1210. base64_decode_bs(BinarySink_UPCAST(Blob), Data);
  1211. }
  1212. else
  1213. {
  1214. // Otherwise, try to decode as if it was a public key _file_.
  1215. BinarySource Src[1];
  1216. BinarySource_BARE_INIT_PL(Src, Data);
  1217. const char * Error;
  1218. if (!ppk_loadpub_s(Src, NULL, BinarySink_UPCAST(Blob), NULL, &Error))
  1219. {
  1220. throw Exception(FMTLOAD(SSH_HOST_CA_DECODE_ERROR, (Error)));
  1221. }
  1222. }
  1223. ptrlen AlgNamePtrLen = pubkey_blob_to_alg_name(ptrlen_from_strbuf(Blob));
  1224. if (!AlgNamePtrLen.len)
  1225. {
  1226. throw Exception(LoadStr(SSH_HOST_CA_NO_KEY_TYPE));
  1227. }
  1228. UnicodeString AlgName = UnicodeString(AnsiString(static_cast<const char *>(AlgNamePtrLen.ptr), AlgNamePtrLen.len));
  1229. const ssh_keyalg * Alg = find_pubkey_alg_len(AlgNamePtrLen);
  1230. if (Alg == NULL)
  1231. {
  1232. throw Exception(FMTLOAD(PUB_KEY_UNKNOWN, (AlgName)));
  1233. }
  1234. if (Alg->is_certificate)
  1235. {
  1236. throw Exception(FMTLOAD(SSH_HOST_CA_CERTIFICATE, (AlgName)));
  1237. }
  1238. ssh_key * Key = ssh_key_new_pub(Alg, ptrlen_from_strbuf(Blob));
  1239. if (Key == NULL)
  1240. {
  1241. throw Exception(FMTLOAD(SSH_HOST_CA_INVALID, (AlgName)));
  1242. }
  1243. char * FingerprintPtr = ssh2_fingerprint(Key, SSH_FPTYPE_DEFAULT);
  1244. Fingerprint = UnicodeString(FingerprintPtr);
  1245. sfree(FingerprintPtr);
  1246. ssh_key_free(Key);
  1247. PublicKey = StrBufToString(Blob);
  1248. }
  1249. __finally
  1250. {
  1251. strbuf_free(Blob);
  1252. }
  1253. }
  1254. //---------------------------------------------------------------------------
  1255. bool IsCertificateValidityExpressionValid(
  1256. const UnicodeString & Str, UnicodeString & Error, int & ErrorStart, int & ErrorLen)
  1257. {
  1258. char * ErrorMsg;
  1259. ptrlen ErrorLoc;
  1260. AnsiString StrAnsi(Str);
  1261. const char * StrPtr = StrAnsi.c_str();
  1262. bool Result = cert_expr_valid(StrPtr, &ErrorMsg, &ErrorLoc);
  1263. if (!Result)
  1264. {
  1265. Error = UnicodeString(ErrorMsg);
  1266. sfree(ErrorMsg);
  1267. ErrorStart = static_cast<const char *>(ErrorLoc.ptr) - StrPtr;
  1268. ErrorLen = ErrorLoc.len;
  1269. }
  1270. return Result;
  1271. }
  1272. //---------------------------------------------------------------------------
  1273. bool IsOpenSSH(const UnicodeString & SshImplementation)
  1274. {
  1275. return
  1276. // e.g. "OpenSSH_5.3"
  1277. (SshImplementation.Pos(L"OpenSSH") == 1) ||
  1278. // Sun SSH is based on OpenSSH (suffers the same bugs)
  1279. (SshImplementation.Pos(L"Sun_SSH") == 1);
  1280. }
  1281. //---------------------------------------------------------------------------
  1282. // Same order as DefaultCipherList
  1283. struct TCipherGroup
  1284. {
  1285. int CipherGroup;
  1286. const ssh2_ciphers * Cipher;
  1287. };
  1288. static TCipherGroup Ciphers[] =
  1289. {
  1290. { CIPHER_AES, &ssh2_aes },
  1291. { CIPHER_CHACHA20, &ssh2_ccp },
  1292. { CIPHER_AESGCM, &ssh2_aesgcm },
  1293. { CIPHER_3DES, &ssh2_3des },
  1294. { CIPHER_DES, &ssh2_des },
  1295. { CIPHER_BLOWFISH, &ssh2_blowfish },
  1296. { CIPHER_ARCFOUR, &ssh2_arcfour },
  1297. };
  1298. //---------------------------------------------------------------------------
  1299. TStrings * SshCipherList()
  1300. {
  1301. std::unique_ptr<TStrings> Result(new TStringList());
  1302. for (unsigned int Index = 0; Index < LENOF(Ciphers); Index++)
  1303. {
  1304. const ssh2_ciphers * Cipher = Ciphers[Index].Cipher;
  1305. for (int Index2 = 0; Index2 < Cipher->nciphers; Index2++)
  1306. {
  1307. UnicodeString Name = UnicodeString(Cipher->list[Index2]->ssh2_id);
  1308. Result->Add(Name);
  1309. }
  1310. }
  1311. return Result.release();
  1312. }
  1313. //---------------------------------------------------------------------------
  1314. int GetCipherGroup(const ssh_cipher * TheCipher)
  1315. {
  1316. DebugAssert(strlen(TheCipher->vt->ssh2_id) > 0);
  1317. for (unsigned int Index = 0; Index < LENOF(Ciphers); Index++)
  1318. {
  1319. TCipherGroup & CipherGroup = Ciphers[Index];
  1320. const ssh2_ciphers * Cipher = CipherGroup.Cipher;
  1321. for (int Index2 = 0; Index2 < Cipher->nciphers; Index2++)
  1322. {
  1323. if (strcmp(TheCipher->vt->ssh2_id, Cipher->list[Index2]->ssh2_id) == 0)
  1324. {
  1325. return CipherGroup.CipherGroup;
  1326. }
  1327. }
  1328. }
  1329. DebugFail();
  1330. return -1;
  1331. }
  1332. //---------------------------------------------------------------------------
  1333. TStrings * SshKexList()
  1334. {
  1335. std::unique_ptr<TStrings> Result(new TStringList());
  1336. // Same order as DefaultKexList
  1337. const ssh_kexes * Kexes[] = {
  1338. &ssh_gssk5_ecdh_kex, &ssh_gssk5_sha2_kex, &ssh_gssk5_sha1_kex,
  1339. &ssh_ntru_hybrid_kex, &ssh_mlkem_curve25519_hybrid_kex, &ssh_mlkem_nist_hybrid_kex, &ssh_ecdh_kex, &ssh_diffiehellman_gex,
  1340. &ssh_diffiehellman_group18, &ssh_diffiehellman_group17, &ssh_diffiehellman_group16, &ssh_diffiehellman_group15, &ssh_diffiehellman_group14,
  1341. &ssh_rsa_kex, &ssh_diffiehellman_group1 };
  1342. for (unsigned int Index = 0; Index < LENOF(Kexes); Index++)
  1343. {
  1344. for (int Index2 = 0; Index2 < Kexes[Index]->nkexes; Index2++)
  1345. {
  1346. UnicodeString Name = UnicodeString(Kexes[Index]->list[Index2]->name);
  1347. Result->Add(Name);
  1348. }
  1349. }
  1350. return Result.release();
  1351. }
  1352. //---------------------------------------------------------------------------
  1353. int HostKeyToPutty(THostKey HostKey)
  1354. {
  1355. int Result;
  1356. switch (HostKey)
  1357. {
  1358. case hkWarn: Result = HK_WARN; break;
  1359. case hkRSA: Result = HK_RSA; break;
  1360. case hkDSA: Result = hkDSA; break;
  1361. case hkECDSA: Result = HK_ECDSA; break;
  1362. case hkED25519: Result = HK_ED25519; break;
  1363. case hkED448: Result = HK_ED448; break;
  1364. default: Result = -1; DebugFail();
  1365. }
  1366. return Result;
  1367. }
  1368. //---------------------------------------------------------------------------
  1369. TStrings * SshHostKeyList()
  1370. {
  1371. std::unique_ptr<TStrings> Result(new TStringList());
  1372. for (int DefaultIndex = 0; DefaultIndex < HOSTKEY_COUNT; DefaultIndex++)
  1373. {
  1374. int Type = HostKeyToPutty(DefaultHostKeyList[DefaultIndex]);
  1375. cp_ssh_keyalg * SignKeys;
  1376. int Count;
  1377. get_hostkey_algs(Type, &Count, &SignKeys);
  1378. try
  1379. {
  1380. for (int Index = 0; Index < Count; Index++)
  1381. {
  1382. cp_ssh_keyalg SignKey = SignKeys[Index];
  1383. UnicodeString Name = UnicodeString(SignKey->ssh_id);
  1384. Result->Add(Name);
  1385. }
  1386. }
  1387. __finally
  1388. {
  1389. sfree(SignKeys);
  1390. }
  1391. }
  1392. return Result.release();
  1393. }
  1394. //---------------------------------------------------------------------------
  1395. TStrings * SshMacList()
  1396. {
  1397. std::unique_ptr<TStrings> Result(new TStringList());
  1398. const struct ssh2_macalg * const * Macs = NULL;
  1399. int Count = 0;
  1400. get_macs(&Count, &Macs);
  1401. for (int Index = 0; Index < Count; Index++)
  1402. {
  1403. UnicodeString Name = UnicodeString(Macs[Index]->name);
  1404. UnicodeString S = Name;
  1405. UnicodeString ETMName = UnicodeString(Macs[Index]->etm_name);
  1406. if (!ETMName.IsEmpty())
  1407. {
  1408. S = FORMAT(L"%s (%s)", (S, ETMName));
  1409. }
  1410. Result->Add(S);
  1411. }
  1412. return Result.release();
  1413. }
  1414. //---------------------------------------------------------------------------
  1415. UnicodeString GetCipherName(const ssh_cipher * Cipher)
  1416. {
  1417. return UnicodeString(UTF8String(Cipher->vt->text_name));
  1418. }
  1419. //---------------------------------------------------------------------------
  1420. UnicodeString GetCompressorName(const ssh_compressor * Compressor)
  1421. {
  1422. UnicodeString Result;
  1423. if (Compressor != NULL)
  1424. {
  1425. Result = UnicodeString(UTF8String(Compressor->vt->name));
  1426. }
  1427. return Result;
  1428. }
  1429. //---------------------------------------------------------------------------
  1430. UnicodeString GetDecompressorName(const ssh_decompressor * Decompressor)
  1431. {
  1432. UnicodeString Result;
  1433. if (Decompressor != NULL)
  1434. {
  1435. Result = UnicodeString(UTF8String(Decompressor->vt->name));
  1436. }
  1437. return Result;
  1438. }
  1439. //---------------------------------------------------------------------------
  1440. void WritePuttySettings(THierarchicalStorage * Storage, const UnicodeString & ASettings)
  1441. {
  1442. if (PuttyRegistryTypes.empty())
  1443. {
  1444. TGuard Guard(PuttyStorageSection.get());
  1445. TValueRestorer<TPuttyRegistryMode> PuttyRegistryModeRestorer(PuttyRegistryMode, prmCollect);
  1446. Conf * conf = conf_new();
  1447. try
  1448. {
  1449. do_defaults(NULL, conf);
  1450. save_settings(NULL, conf);
  1451. }
  1452. __finally
  1453. {
  1454. conf_free(conf);
  1455. }
  1456. }
  1457. std::unique_ptr<TStrings> Settings(new TStringList());
  1458. UnicodeString Buf = ASettings;
  1459. UnicodeString Setting;
  1460. while (CutToken(Buf, Setting))
  1461. {
  1462. Settings->Add(Setting);
  1463. }
  1464. for (int Index = 0; Index < Settings->Count; Index++)
  1465. {
  1466. UnicodeString Name = Settings->Names[Index];
  1467. TPuttyRegistryTypes::const_iterator IType = PuttyRegistryTypes.find(Name);
  1468. if (IType != PuttyRegistryTypes.end())
  1469. {
  1470. UnicodeString Value = Settings->ValueFromIndex[Index];
  1471. int I = 0; // shut up
  1472. if (IType->second == REG_SZ)
  1473. {
  1474. Storage->WriteStringRaw(Name, Value);
  1475. }
  1476. else if (DebugAlwaysTrue(IType->second == REG_DWORD) &&
  1477. TryStrToInt(Value, I))
  1478. {
  1479. Storage->WriteInteger(Name, I);
  1480. }
  1481. }
  1482. }
  1483. }
  1484. //---------------------------------------------------------------------------
  1485. void PuttyDefaults(Conf * conf)
  1486. {
  1487. TGuard Guard(PuttyStorageSection.get());
  1488. TValueRestorer<TPuttyRegistryMode> PuttyRegistryModeRestorer(PuttyRegistryMode, prmFail);
  1489. do_defaults(NULL, conf);
  1490. }
  1491. //---------------------------------------------------------------------------
  1492. void SavePuttyDefaults(const UnicodeString & Name)
  1493. {
  1494. TGuard Guard(PuttyStorageSection.get());
  1495. TValueRestorer<TPuttyRegistryMode> PuttyRegistryModeRestorer(PuttyRegistryMode, prmPass);
  1496. Conf * conf = conf_new();
  1497. try
  1498. {
  1499. PuttyDefaults(conf);
  1500. AnsiString PuttyName = PuttyStr(Name);
  1501. save_settings(PuttyName.c_str(), conf);
  1502. }
  1503. __finally
  1504. {
  1505. conf_free(conf);
  1506. }
  1507. }
  1508. //---------------------------------------------------------------------------
  1509. struct host_ca_enum
  1510. {
  1511. int Index;
  1512. };
  1513. //---------------------------------------------------------------------------
  1514. host_ca_enum * enum_host_ca_start()
  1515. {
  1516. Configuration->RefreshPuttySshHostCAList();
  1517. host_ca_enum * Result = new host_ca_enum();
  1518. Result->Index = 0;
  1519. return Result;
  1520. }
  1521. //---------------------------------------------------------------------------
  1522. bool enum_host_ca_next(host_ca_enum * Enum, strbuf * StrBuf)
  1523. {
  1524. const TSshHostCAList * SshHostCAList = Configuration->ActiveSshHostCAList;
  1525. bool Result = (Enum->Index < SshHostCAList->GetCount());
  1526. if (Result)
  1527. {
  1528. put_asciz(StrBuf, UTF8String(SshHostCAList->Get(Enum->Index)->Name).c_str());
  1529. Enum->Index++;
  1530. }
  1531. return Result;
  1532. }
  1533. //---------------------------------------------------------------------------
  1534. void enum_host_ca_finish(host_ca_enum * Enum)
  1535. {
  1536. delete Enum;
  1537. }
  1538. //---------------------------------------------------------------------------
  1539. host_ca * host_ca_load(const char * NameStr)
  1540. {
  1541. host_ca * Result = NULL;
  1542. UnicodeString Name = UTF8String(NameStr);
  1543. const TSshHostCA * SshHostCA = Configuration->ActiveSshHostCAList->Find(Name);
  1544. if (DebugAlwaysTrue(SshHostCA != NULL))
  1545. {
  1546. Result = host_ca_new();
  1547. Result->name = dupstr(NameStr);
  1548. Result->ca_public_key = strbuf_dup(make_ptrlen(SshHostCA->PublicKey.c_str(), SshHostCA->PublicKey.Length()));
  1549. Result->validity_expression = dupstr(UTF8String(SshHostCA->ValidityExpression).c_str());
  1550. Result->opts.permit_rsa_sha1 = SshHostCA->PermitRsaSha1;
  1551. Result->opts.permit_rsa_sha256 = SshHostCA->PermitRsaSha256;
  1552. Result->opts.permit_rsa_sha512 = SshHostCA->PermitRsaSha512;
  1553. }
  1554. return Result;
  1555. }
  1556. //---------------------------------------------------------------------------
  1557. //---------------------------------------------------------------------------