ソースを参照

Bug 1562: Configurable priority of host key types

https://winscp.net/tracker/1562

Source commit: 9076a75e5807f1572c9aaee51047d85248ab3b88
Martin Prikryl 8 年 前
コミット
091b6ab129

+ 30 - 52
source/core/SecureShell.cpp

@@ -210,6 +210,21 @@ Conf * __fastcall TSecureShell::StoreToConfig(TSessionData * Data, bool Simple)
     conf_set_int_int(conf, CONF_ssh_kexlist, k, pkex);
   }
 
+  DebugAssert(HK_MAX == HOSTKEY_COUNT);
+  for (int h = 0; h < HOSTKEY_COUNT; h++)
+  {
+    int phk;
+    switch (Data->HostKeys[h]) {
+      case hkWarn: phk = HK_WARN; break;
+      case hkRSA: phk = HK_RSA; break;
+      case hkDSA: phk = hkDSA; break;
+      case hkECDSA: phk = HK_ECDSA; break;
+      case hkED25519: phk = HK_ED25519; break;
+      default: DebugFail();
+    }
+    conf_set_int_int(conf, CONF_ssh_hklist, h, phk);
+  }
+
   DebugAssert(ngsslibs == GSSLIB_COUNT);
   for (int g = 0; g < GSSLIB_COUNT; g++)
   {
@@ -360,13 +375,6 @@ Conf * __fastcall TSecureShell::StoreToConfig(TSessionData * Data, bool Simple)
   conf_set_int(conf, CONF_ssh_show_banner, TRUE);
   conf_set_int(conf, CONF_proxy_log_to_term, FORCE_OFF);
 
-  conf_set_int_int(conf, CONF_ssh_hklist, 0, HK_ED25519);
-  conf_set_int_int(conf, CONF_ssh_hklist, 1, HK_ECDSA);
-  conf_set_int_int(conf, CONF_ssh_hklist, 2, HK_RSA);
-  conf_set_int_int(conf, CONF_ssh_hklist, 3, HK_DSA);
-  conf_set_int_int(conf, CONF_ssh_hklist, 4, HK_WARN);
-  DebugAssert(HK_MAX == 5);
-
   conf_set_str(conf, CONF_loghost, AnsiString(Data->LogicalHostName).c_str());
 
   return conf;
@@ -2383,55 +2391,25 @@ bool __fastcall TSecureShell::HaveHostKey(UnicodeString Host, int Port, const Un
   return Result;
 }
 //---------------------------------------------------------------------------
-void __fastcall TSecureShell::AskAlg(const UnicodeString AlgType,
-  const UnicodeString AlgName)
+void __fastcall TSecureShell::AskAlg(UnicodeString AlgType, UnicodeString AlgName)
 {
-  UnicodeString Msg;
-  UnicodeString Error;
-  if (AlgType == L"key-exchange algorithm")
-  {
-    Msg = FMTLOAD(KEX_BELOW_TRESHOLD, (AlgName));
-    Error = FMTLOAD(KEX_NOT_VERIFIED, (AlgName));
-  }
-  else if (AlgType == L"hostkey type")
-  {
-    // noop as we do not allow host key algorithm configuration,
-    // so no algorithm can get below WARN level
-  }
-  else
-  {
-    int CipherType;
-    if (AlgType == L"cipher")
-    {
-      CipherType = CIPHER_TYPE_BOTH;
-    }
-    else if (AlgType == L"client-to-server cipher")
-    {
-      CipherType = CIPHER_TYPE_CS;
-    }
-    else if (AlgType == L"server-to-client cipher")
-    {
-      CipherType = CIPHER_TYPE_SC;
-    }
-    else
-    {
-      DebugFail();
-      CipherType = 0;
-    }
+  // beware of changing order
+  static const TPuttyTranslation AlgTranslation[] = {
+    { L"cipher", CIPHER_TYPE_BOTH2 },
+    { L"client-to-server cipher", CIPHER_TYPE_CS2 },
+    { L"server-to-client cipher", CIPHER_TYPE_SC2 },
+    { L"key-exchange algorithm", KEY_EXCHANGE_ALG },
+    { L"hostkey type", KEYKEY_TYPE },
+  };
 
-    if (CipherType != 0)
-    {
-      Msg = FMTLOAD(CIPHER_BELOW_TRESHOLD, (LoadStr(CipherType), AlgName));
-      Error = FMTLOAD(CIPHER_NOT_VERIFIED, (AlgName));
-    }
-  }
+  TranslatePuttyMessage(AlgTranslation, LENOF(AlgTranslation), AlgType);
 
-  if (!Msg.IsEmpty())
+  UnicodeString Msg = FMTLOAD(ALG_BELOW_TRESHOLD, (AlgType, AlgName));
+
+  if (FUI->QueryUser(Msg, NULL, qaYes | qaNo, NULL, qtWarning) == qaNo)
   {
-    if (FUI->QueryUser(Msg, NULL, qaYes | qaNo, NULL, qtWarning) == qaNo)
-    {
-      FUI->FatalError(NULL, Error);
-    }
+    UnicodeString Error = FMTLOAD(ALG_NOT_VERIFIED, (AlgType, AlgName));
+    FUI->FatalError(NULL, Error);
   }
 }
 //---------------------------------------------------------------------------

+ 1 - 1
source/core/SecureShell.h

@@ -159,7 +159,7 @@ public:
   void __fastcall VerifyHostKey(UnicodeString Host, int Port,
     const UnicodeString KeyType, UnicodeString KeyStr, UnicodeString Fingerprint);
   bool __fastcall HaveHostKey(UnicodeString Host, int Port, const UnicodeString KeyType);
-  void __fastcall AskAlg(const UnicodeString AlgType, const UnicodeString AlgName);
+  void __fastcall AskAlg(UnicodeString AlgType, UnicodeString AlgName);
   void __fastcall DisplayBanner(const UnicodeString & Banner);
   void __fastcall OldKeyfileWarning();
   void __fastcall PuttyLogEvent(const char * Str);

+ 48 - 0
source/core/SessionData.cpp

@@ -25,12 +25,15 @@ const wchar_t * ProxyMethodNames = L"None;SOCKS4;SOCKS5;HTTP;Telnet;Cmd";
 const wchar_t * DefaultName = L"Default Settings";
 const UnicodeString CipherNames[CIPHER_COUNT] = {L"WARN", L"3des", L"blowfish", L"aes", L"des", L"arcfour", L"chacha20"};
 const UnicodeString KexNames[KEX_COUNT] = {L"WARN", L"dh-group1-sha1", L"dh-group14-sha1", L"dh-gex-sha1", L"rsa", L"ecdh"};
+const UnicodeString HostKeyNames[HOSTKEY_COUNT] = {L"WARN", L"rsa", L"dsa", L"ecdsa", L"ed25519"};
 const UnicodeString GssLibNames[GSSLIB_COUNT] = {L"gssapi32", L"sspi", L"custom"};
 const wchar_t SshProtList[][10] = {L"1", L"1>2", L"2>1", L"2"};
 const TCipher DefaultCipherList[CIPHER_COUNT] =
   { cipAES, cipChaCha20, cipBlowfish, cip3DES, cipWarn, cipArcfour, cipDES };
 const TKex DefaultKexList[KEX_COUNT] =
   { kexECDH, kexDHGEx, kexDHGroup14, kexRSA, kexWarn, kexDHGroup1 };
+const THostKey DefaultHostKeyList[HOSTKEY_COUNT] =
+  { hkED25519, hkECDSA, hkRSA, hkDSA, hkWarn };
 const TGssLib DefaultGssLibList[GSSLIB_COUNT] =
   { gssGssApi32, gssSspi, gssCustom };
 const wchar_t FSProtocolNames[FSPROTOCOL_COUNT][16] = { L"SCP", L"SFTP (SCP)", L"SFTP", L"", L"", L"FTP", L"WebDAV" };
@@ -132,6 +135,10 @@ void __fastcall TSessionData::Default()
   {
     Kex[Index] = DefaultKexList[Index];
   }
+  for (int Index = 0; Index < HOSTKEY_COUNT; Index++)
+  {
+    HostKeys[Index] = DefaultHostKeyList[Index];
+  }
   for (int Index = 0; Index < GSSLIB_COUNT; Index++)
   {
     GssLib[Index] = DefaultGssLibList[Index];
@@ -299,6 +306,7 @@ void __fastcall TSessionData::NonPersistant()
   PROPERTY(SshNoUserAuth); \
   PROPERTY(CipherList); \
   PROPERTY(KexList); \
+  PROPERTY(HostKeyList); \
   PROPERTY(GssLibList); \
   PROPERTY(GssLibCustom); \
   PROPERTY(AddressFamily); \
@@ -575,6 +583,7 @@ void __fastcall TSessionData::DoLoad(THierarchicalStorage * Storage, bool PuttyI
   SshNoUserAuth = Storage->ReadBool(L"SshNoUserAuth", SshNoUserAuth);
   CipherList = Storage->ReadString(L"Cipher", CipherList);
   KexList = Storage->ReadString(L"KEX", KexList);
+  HostKeyList = Storage->ReadString(L"HostKey", HostKeyList);
   GssLibList = Storage->ReadString(L"GSSLibs", GssLibList);
   GssLibCustom = Storage->ReadString(L"GSSCustom", GssLibCustom);
   PublicKeyFile = Storage->ReadString(L"PublicKeyFile", PublicKeyFile);
@@ -778,6 +787,17 @@ void __fastcall TSessionData::DoLoad(THierarchicalStorage * Storage, bool PuttyI
   CIPHER_TEST(L"aes,blowfish,chacha20,3des,WARN,des", L"aes,blowfish,chacha20,3des,WARN,des,arcfour");
   #undef CIPHER_DEFAULT
   #undef CIPHER_TEST
+
+  #define HOSTKEY_TEST(VALUE, EXPECTED) HostKeyList = VALUE; DebugAssert(HostKeyList == EXPECTED);
+  #define HOSTKEY_DEFAULT L"ed25519,ecdsa,rsa,dsa,WARN"
+  // Empty source should result in default list
+  HOSTKEY_TEST(L"", HOSTKEY_DEFAULT);
+  // Missing priority algo
+  HOSTKEY_TEST(L"ecdsa,rsa,dsa,WARN", HOSTKEY_DEFAULT);
+  // Missing non-priority algo
+  HOSTKEY_TEST(L"ed25519,ecdsa,dsa,WARN", L"ed25519,ecdsa,dsa,rsa,WARN");
+  #undef HOSTKEY_DEFAULT
+  #undef HOSTKEY_TEST
 #endif
 }
 //---------------------------------------------------------------------
@@ -886,6 +906,7 @@ void __fastcall TSessionData::DoSave(THierarchicalStorage * Storage,
   WRITE_DATA(Bool, SshNoUserAuth);
   WRITE_DATA_EX(String, L"Cipher", CipherList, );
   WRITE_DATA_EX(String, L"KEX", KexList, );
+  WRITE_DATA_EX(String, L"HostKey", HostKeyList, );
   WRITE_DATA_EX(String, L"GSSLibs", GssLibList, );
   WRITE_DATA_EX(String, L"GSSCustom", GssLibCustom, );
   WRITE_DATA(Integer, AddressFamily);
@@ -2446,6 +2467,33 @@ UnicodeString __fastcall TSessionData::GetKexList() const
   return Result;
 }
 //---------------------------------------------------------------------
+void __fastcall TSessionData::SetHostKeys(int Index, THostKey value)
+{
+  DebugAssert(Index >= 0 && Index < HOSTKEY_COUNT);
+  SET_SESSION_PROPERTY(HostKeys[Index]);
+}
+//---------------------------------------------------------------------
+THostKey __fastcall TSessionData::GetHostKeys(int Index) const
+{
+  DebugAssert(Index >= 0 && Index < HOSTKEY_COUNT);
+  return FHostKeys[Index];
+}
+//---------------------------------------------------------------------
+void __fastcall TSessionData::SetHostKeyList(UnicodeString value)
+{
+  SetAlgoList(FHostKeys, DefaultHostKeyList, HostKeyNames, HOSTKEY_COUNT, hkWarn, value);
+}
+//---------------------------------------------------------------------
+UnicodeString __fastcall TSessionData::GetHostKeyList() const
+{
+  UnicodeString Result;
+  for (int Index = 0; Index < HOSTKEY_COUNT; Index++)
+  {
+    Result += UnicodeString(Index ? L"," : L"") + HostKeyNames[HostKeys[Index]];
+  }
+  return Result;
+}
+//---------------------------------------------------------------------
 void __fastcall TSessionData::SetGssLib(int Index, TGssLib value)
 {
   DebugAssert(Index >= 0 && Index < GSSLIB_COUNT);

+ 11 - 0
source/core/SessionData.h

@@ -23,6 +23,8 @@ enum TProxyMethod { pmNone, pmSocks4, pmSocks5, pmHTTP, pmTelnet, pmCmd };
 enum TSshProt { ssh1only, ssh1deprecated, ssh2deprecated, ssh2only };
 enum TKex { kexWarn, kexDHGroup1, kexDHGroup14, kexDHGEx, kexRSA, kexECDH };
 #define KEX_COUNT (kexECDH+1)
+enum THostKey { hkWarn, hkRSA, hkDSA, hkECDSA, hkED25519, hkMax };
+#define HOSTKEY_COUNT (hkMax)
 enum TGssLib { gssGssApi32, gssSspi, gssCustom };
 #define GSSLIB_COUNT (gssCustom+1)
 // names have to match PuTTY registry entries (see settings.c)
@@ -50,10 +52,12 @@ enum TSessionUrlFlags
 //---------------------------------------------------------------------------
 extern const UnicodeString CipherNames[CIPHER_COUNT];
 extern const UnicodeString KexNames[KEX_COUNT];
+extern const UnicodeString HostKeyNames[HOSTKEY_COUNT];
 extern const UnicodeString GssLibNames[GSSLIB_COUNT];
 extern const wchar_t SshProtList[][10];
 extern const TCipher DefaultCipherList[CIPHER_COUNT];
 extern const TKex DefaultKexList[KEX_COUNT];
+extern const THostKey DefaultHostKeyList[HOSTKEY_COUNT];
 extern const TGssLib DefaultGssLibList[GSSLIB_COUNT];
 extern const wchar_t FSProtocolNames[FSPROTOCOL_COUNT][16];
 extern const int DefaultSendBuf;
@@ -113,6 +117,7 @@ private:
   bool FSshNoUserAuth;
   TCipher FCiphers[CIPHER_COUNT];
   TKex FKex[KEX_COUNT];
+  THostKey FHostKeys[HOSTKEY_COUNT];
   TGssLib FGssLib[GSSLIB_COUNT];
   UnicodeString FGssLibCustom;
   bool FClearAliases;
@@ -244,6 +249,8 @@ private:
   TCipher __fastcall GetCipher(int Index) const;
   void __fastcall SetKex(int Index, TKex value);
   TKex __fastcall GetKex(int Index) const;
+  void __fastcall SetHostKeys(int Index, THostKey value);
+  THostKey __fastcall GetHostKeys(int Index) const;
   void __fastcall SetGssLib(int Index, TGssLib value);
   TGssLib __fastcall GetGssLib(int Index) const;
   void __fastcall SetGssLibCustom(UnicodeString value);
@@ -300,6 +307,8 @@ private:
   UnicodeString __fastcall GetCipherList() const;
   void __fastcall SetKexList(UnicodeString value);
   UnicodeString __fastcall GetKexList() const;
+  void __fastcall SetHostKeyList(UnicodeString value);
+  UnicodeString __fastcall GetHostKeyList() const;
   void __fastcall SetGssLibList(UnicodeString value);
   UnicodeString __fastcall GetGssLibList() const;
   void __fastcall SetProxyMethod(TProxyMethod value);
@@ -495,6 +504,7 @@ public:
   __property bool SshNoUserAuth  = { read=FSshNoUserAuth, write=SetSshNoUserAuth };
   __property TCipher Cipher[int Index] = { read=GetCipher, write=SetCipher };
   __property TKex Kex[int Index] = { read=GetKex, write=SetKex };
+  __property THostKey HostKeys[int Index] = { read=GetHostKeys, write=SetHostKeys };
   __property TGssLib GssLib[int Index] = { read=GetGssLib, write=SetGssLib };
   __property UnicodeString GssLibCustom = { read=FGssLibCustom, write=SetGssLibCustom };
   __property UnicodeString PublicKeyFile  = { read=FPublicKeyFile, write=SetPublicKeyFile };
@@ -541,6 +551,7 @@ public:
   __property UnicodeString SshProtStr  = { read=GetSshProtStr };
   __property UnicodeString CipherList  = { read=GetCipherList, write=SetCipherList };
   __property UnicodeString KexList  = { read=GetKexList, write=SetKexList };
+  __property UnicodeString HostKeyList  = { read=GetHostKeyList, write=SetHostKeyList };
   __property UnicodeString GssLibList  = { read=GetGssLibList, write=SetGssLibList };
   __property TProxyMethod ProxyMethod  = { read=FProxyMethod, write=SetProxyMethod };
   __property UnicodeString ProxyHost  = { read=FProxyHost, write=SetProxyHost };

+ 7 - 7
source/resource/TextsCore.h

@@ -238,8 +238,7 @@
 #define SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK 713
 #define KEY_TYPE_UNOPENABLE     714
 #define UNKNOWN_CHECKSUM        715
-#define CIPHER_NOT_VERIFIED     716
-#define KEX_NOT_VERIFIED        717
+#define ALG_NOT_VERIFIED        716
 #define SFTP_STATUS_4           718
 #define CERTIFICATE_OPEN_ERROR  719
 #define CERTIFICATE_READ_ERROR  720
@@ -270,10 +269,10 @@
 #define PROMPT_KEY_PASSPHRASE   303
 #define FILE_OVERWRITE          304
 #define DIRECTORY_OVERWRITE     305
-#define CIPHER_BELOW_TRESHOLD   306
-#define CIPHER_TYPE_BOTH        307
-#define CIPHER_TYPE_CS          308
-#define CIPHER_TYPE_SC          309
+#define ALG_BELOW_TRESHOLD      306
+#define CIPHER_TYPE_BOTH2       307
+#define CIPHER_TYPE_CS2         308
+#define CIPHER_TYPE_SC2         309
 #define RESUME_TRANSFER2        310
 #define PARTIAL_BIGGER_THAN_SOURCE 311
 #define APPEND_OR_RESUME2       312
@@ -282,7 +281,6 @@
 #define LOCAL_FILE_OVERWRITE2   315
 #define REMOTE_FILE_OVERWRITE2  316
 #define TIMEOUT_STILL_WAITING3  321
-#define KEX_BELOW_TRESHOLD      322
 #define RECONNECT_BUTTON        323
 #define RENAME_BUTTON           324
 #define TUNNEL_SESSION_NAME     327
@@ -312,6 +310,8 @@
 #define CERTIFICATE_PASSPHRASE_TITLE 351
 #define KEY_TYPE_CONVERT3       352
 #define MULTI_FILES_TO_ONE      353
+#define KEY_EXCHANGE_ALG        354
+#define KEYKEY_TYPE             355
 
 #define CORE_INFORMATION_STRINGS 400
 #define YES_STR                 401

+ 7 - 7
source/resource/TextsCore1.rc

@@ -208,8 +208,7 @@ BEGIN
   SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK, "The requested operation could not be completed because the specifed byte range lock has not been granted."
   KEY_TYPE_UNOPENABLE, "Private key file '%s' does not exist or cannot be opened."
   UNKNOWN_CHECKSUM, "Checksum algorithm '%s' is not supported."
-  CIPHER_NOT_VERIFIED, "Cipher %s was not verified!"
-  KEX_NOT_VERIFIED, "Key-exchange algorithm %s was not verified!"
+  ALG_NOT_VERIFIED, "The %s %s was not verified!"
   SFTP_STATUS_4, "Common reasons for the Error code 4 are:\n- Renaming a file to a name of already existing file.\n- Creating a directory that already exists.\n- Moving a remote file to a different filesystem (HDD).\n- Uploading a file to a full filesystem (HDD).\n- Exceeding a user disk quota."
   CERTIFICATE_OPEN_ERROR, "Cannot open certificate \"%s\"."
   CERTIFICATE_READ_ERROR, "Cannot read certificate \"%s\"."
@@ -240,10 +239,10 @@ BEGIN
   PROMPT_KEY_PASSPHRASE, "&Passphrase for key '%s':"
   FILE_OVERWRITE, "File '%s' already exists. Overwrite?"
   DIRECTORY_OVERWRITE, "Directory '%s' already exists. Overwrite?"
-  CIPHER_BELOW_TRESHOLD, "The first %scipher supported by the server is %s, which is below the configured warning threshold.\n\nDo you want to continue with this connection?"
-  CIPHER_TYPE_BOTH, ""
-  CIPHER_TYPE_CS, "client-to-server "
-  CIPHER_TYPE_SC, "server-to-client "
+  ALG_BELOW_TRESHOLD, "The first %s supported by the server is %s, which is below the configured warning threshold.\n\nDo you want to continue with this connection?"
+  CIPHER_TYPE_BOTH2, "cipher"
+  CIPHER_TYPE_CS2, "client-to-server cipher"
+  CIPHER_TYPE_SC2, "server-to-client cipher"
   RESUME_TRANSFER2, "**Do you want to resume file transfer?**\n\nTarget directory contains partially transferred file '%s'.\n\nNote: Answering 'No' would delete partially transferred file and restart transfer."
   PARTIAL_BIGGER_THAN_SOURCE, "Target directory contains partially transferred file '%s', which is bigger than a source file. The file will be deleted."
   APPEND_OR_RESUME2, "**Do you want to append file '%s' at the end of existing file?**\n\nPress 'No' to resume file transfer instead."
@@ -252,7 +251,6 @@ BEGIN
   LOCAL_FILE_OVERWRITE2, "**Overwrite local file '%s'?**\n\nDestination directory already contains file '%s'.\nChoose, if you want to overwrite the file or skip this transfer and keep existing file."
   REMOTE_FILE_OVERWRITE2, "**Overwrite remote file '%s'?**\n\nDestination directory already contains file '%s'.\nChoose, if you want to overwrite the file or skip this transfer and keep existing file."
   TIMEOUT_STILL_WAITING3, "Host is not communicating for more than %d seconds.\nStill waiting...\n\nNote: If the problem repeats, try turning off 'Optimize connection buffer size'."
-  KEX_BELOW_TRESHOLD, "The first key-exchange algorithm supported by the server is %s, which is below the configured warning threshold.\n\nDo you want to continue with this connection?"
   RECONNECT_BUTTON, "&Reconnect"
   RENAME_BUTTON, "New na&me"
   TUNNEL_SESSION_NAME, "Tunnel for %s"
@@ -282,6 +280,8 @@ BEGIN
   CERTIFICATE_PASSPHRASE_TITLE, "Client certificate passphrase"
   KEY_TYPE_CONVERT3, "**Do you want to convert this %s private key to PuTTY format?**\n\n%s"
   MULTI_FILES_TO_ONE, "**Are you sure you want to transfer multiple files to a single file '%s' in a directory '%s'?**\n\nThe files will overwrite one another.\n\nIf you actually want to transfer all files to a directory '%s', keeping their name, make sure you terminate the path with a slash."
+  KEY_EXCHANGE_ALG, "key-exchange algorithm"
+  KEYKEY_TYPE, "host key type"
 
   CORE_INFORMATION_STRINGS, "CORE_INFORMATION"
   YES_STR, "Yes"