Selaa lähdekoodia

Merge branch 'thirdparty_dev' into dev + Plus minimal changes in code needed to make it compile and pass unit tests + Other changes are pending

What is pending:
- AES is now again compiled using Borland compiler (previously by VS)
- Add support for HW SHA

What is lost and needs to be re-implemented:
- Custom fixes for vulnerabilities fixed in PuTTY 0.71 (will be added implicitly, once merge of PuTTY 0.71 is complete)
- callback.c is now locked more heavily - should be optimized.

Note: Or would-be thread-safety changes to callback.c were obviously not thread safe as they would call callbacks for other sessions from thread for another session (as there's only one callback queue) - This needs to be solved

# Conflicts:
#	source/putty/WINDOWS/wingss.c
#	source/putty/WINDOWS/winhsock.c
#	source/putty/WINDOWS/winnet.c
#	source/putty/callback.c

#	source/putty/proxy.c
#	source/putty/putty.h
#	source/putty/ssh.c
#	source/putty/ssh.h
#	source/putty/sshaes.c
#	source/putty/sshdh.c
#	source/putty/sshpubk.c
#	source/putty/sshrsa.c
#	source/putty/sshsh256.c
#	source/putty/version.h
#	source/putty/x11fwd.c

Source commit: d7d9798d5b0c01f4a797d0a2ace8856e32909158
Martin Prikryl 6 vuotta sitten
vanhempi
sitoutus
8ce20a988b
71 muutettua tiedostoa jossa 7760 lisäystä ja 6258 poistoa
  1. 21 0
      source/Putty.cbproj
  2. 3 3
      source/core/Cryptography.cpp
  3. 33 35
      source/core/PuttyIntf.cpp
  4. 2 0
      source/core/PuttyIntf.h
  5. 1 1
      source/core/PuttyTools.h
  6. 20 8
      source/core/SecureShell.cpp
  7. 3 3
      source/core/SessionData.cpp
  8. 2 1
      source/putty/be_misc.c
  9. 75 35
      source/putty/callback.c
  10. 29 120
      source/putty/conf.c
  11. 6 6
      source/putty/cproxy.c
  12. 80 0
      source/putty/defs.h
  13. 29 35
      source/putty/errsock.c
  14. 273 438
      source/putty/import.c
  15. 5 0
      source/putty/int64.h
  16. 2 5
      source/putty/logging.c
  17. 236 0
      source/putty/marshal.c
  18. 273 0
      source/putty/marshal.h
  19. 105 45
      source/putty/misc.c
  20. 24 33
      source/putty/misc.h
  21. 10 19
      source/putty/network.h
  22. 42 0
      source/putty/nullplug.c
  23. 17 14
      source/putty/pageant.h
  24. 1 1
      source/putty/pgssapi.c
  25. 37 0
      source/putty/pgssapi.h
  26. 289 286
      source/putty/portfwd.c
  27. 151 155
      source/putty/proxy.c
  28. 17 23
      source/putty/proxy.h
  29. 169 68
      source/putty/putty.h
  30. 6 6
      source/putty/puttyexp.h
  31. 15 0
      source/putty/puttymem.h
  32. 378 1794
      source/putty/ssh.c
  33. 338 169
      source/putty/ssh.h
  34. 294 0
      source/putty/ssh1bpp.c
  35. 76 0
      source/putty/ssh1censor.c
  36. 162 0
      source/putty/ssh2bpp-bare.c
  37. 702 0
      source/putty/ssh2bpp.c
  38. 107 0
      source/putty/ssh2censor.c
  39. 841 388
      source/putty/sshaes.c
  40. 5 4
      source/putty/ssharcf.c
  41. 3 6
      source/putty/sshbcrypt.c
  42. 19 21
      source/putty/sshblowf.c
  43. 3 4
      source/putty/sshblowf.h
  44. 56 61
      source/putty/sshbn.c
  45. 62 0
      source/putty/sshbpp.h
  46. 35 24
      source/putty/sshccp.c
  47. 54 0
      source/putty/sshcr.h
  48. 39 27
      source/putty/sshdes.c
  49. 50 5
      source/putty/sshdh.c
  50. 120 293
      source/putty/sshdss.c
  51. 215 401
      source/putty/sshecc.c
  52. 20 3
      source/putty/sshgss.h
  53. 93 5
      source/putty/sshgssc.c
  54. 1 0
      source/putty/sshgssc.h
  55. 29 16
      source/putty/sshmd5.c
  56. 234 328
      source/putty/sshpubk.c
  57. 194 355
      source/putty/sshrsa.c
  58. 310 35
      source/putty/sshsh256.c
  59. 23 19
      source/putty/sshsh512.c
  60. 350 43
      source/putty/sshsha.c
  61. 275 338
      source/putty/sshshare.c
  62. 59 151
      source/putty/sshzlib.c
  63. 35 6
      source/putty/version.h
  64. 139 12
      source/putty/windows/wingss.c
  65. 102 105
      source/putty/windows/winhsock.c
  66. 84 44
      source/putty/windows/winmisc.c
  67. 106 145
      source/putty/windows/winnet.c
  68. 27 17
      source/putty/windows/winnoise.c
  69. 6 2
      source/putty/windows/winpgntc.c
  70. 37 14
      source/putty/windows/winstuff.h
  71. 101 83
      source/putty/x11fwd.c

+ 21 - 0
source/Putty.cbproj

@@ -123,6 +123,9 @@
 			<BuildOrder>27</BuildOrder>
 			<BuildOrder>11</BuildOrder>
 		</CppCompile>
+		<CppCompile Include="putty\marshal.c">
+			<BuildOrder>55</BuildOrder>
+		</CppCompile>
 		<CppCompile Include="putty\misc.c">
 			<BuildOrder>26</BuildOrder>
 			<BuildOrder>14</BuildOrder>
@@ -133,6 +136,9 @@
 		<CppCompile Include="putty\noshare.c">
 			<BuildOrder>41</BuildOrder>
 		</CppCompile>
+		<CppCompile Include="putty\nullplug.c">
+			<BuildOrder>56</BuildOrder>
+		</CppCompile>
 		<CppCompile Include="putty\pgssapi.c">
 			<BuildOrder>37</BuildOrder>
 		</CppCompile>
@@ -147,6 +153,21 @@
 			<BuildOrder>23</BuildOrder>
 			<BuildOrder>19</BuildOrder>
 		</CppCompile>
+		<CppCompile Include="putty\ssh1bpp.c">
+			<BuildOrder>53</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh1censor.c">
+			<BuildOrder>57</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh2bpp.c">
+			<BuildOrder>54</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh2bpp-bare.c">
+			<BuildOrder>59</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh2censor.c">
+			<BuildOrder>58</BuildOrder>
+		</CppCompile>
 		<CppCompile Include="putty\sshaes.c">
 			<BuildOrder>26</BuildOrder>
 			<BuildOrder>22</BuildOrder>

+ 3 - 3
source/core/Cryptography.cpp

@@ -55,7 +55,7 @@
 
 #define sha1_ctx                  SHA_State
 #define sha1_begin(ctx)           putty_SHA_Init(ctx)
-#define sha1_hash(buf, len, ctx)  SHA_Bytes(ctx, buf, len)
+#define sha1_hash(buf, len, ctx)  put_data(ctx, buf, len)
 #define sha1_end(dig, ctx)        putty_SHA_Final(ctx, dig)
 
 #define IN_BLOCK_LENGTH     64
@@ -156,7 +156,7 @@ static void hmac_sha1_end(unsigned char mac[], unsigned long mac_len, hmac_ctx c
 
 void aes_set_encrypt_key(const unsigned char in_key[], unsigned int klen, void * cx)
 {
-  call_aes_setup(cx, BLOCK_SIZE, const_cast<unsigned char *>(in_key), klen);
+  call_aes_setup(cx, const_cast<unsigned char *>(in_key), klen);
 }
 
 void aes_encrypt_block(const unsigned char in_blk[], unsigned char out_blk[], void * cx)
@@ -672,7 +672,7 @@ TEncryption::~TEncryption()
 //---------------------------------------------------------------------------
 void TEncryption::SetSalt()
 {
-  aes_iv(FContext, reinterpret_cast<unsigned char *>(FSalt.c_str()));
+  aes_iv(FContext, reinterpret_cast<const void *>(FSalt.c_str()));
 }
 //---------------------------------------------------------------------------
 void TEncryption::NeedSalt()

+ 33 - 35
source/core/PuttyIntf.cpp

@@ -82,8 +82,8 @@ extern "C" char * do_select(Plug plug, SOCKET skt, int startup)
   {
     // If it is not SSH/PFwd plug, then it must be Proxy plug.
     // Get SSH/PFwd plug which it wraps.
-    Proxy_Socket ProxySocket = ((Proxy_Plug)plug)->proxy_socket;
-    plug = ProxySocket->plug;
+    ProxySocket * AProxySocket = get_proxy_plug_socket(plug);
+    plug = AProxySocket->plug;
   }
 
   bool pfwd = is_pfwd(plug);
@@ -108,7 +108,7 @@ extern "C" char * do_select(Plug plug, SOCKET skt, int startup)
   return NULL;
 }
 //---------------------------------------------------------------------------
-int from_backend(void * frontend, int is_stderr, const char * data, int datalen)
+int from_backend(void * frontend, int is_stderr, const void * data, int datalen)
 {
   DebugAssert(frontend);
   if (is_stderr >= 0)
@@ -119,12 +119,12 @@ int from_backend(void * frontend, int is_stderr, const char * data, int datalen)
   else
   {
     DebugAssert(is_stderr == -1);
-    ((TSecureShell *)frontend)->CWrite(data, datalen);
+    ((TSecureShell *)frontend)->CWrite(reinterpret_cast<const char *>(data), datalen);
   }
   return 0;
 }
 //---------------------------------------------------------------------------
-int from_backend_untrusted(void * /*frontend*/, const char * /*data*/, int /*len*/)
+int from_backend_untrusted(void * /*frontend*/, const void * /*data*/, int /*len*/)
 {
   // currently used with authentication banner only,
   // for which we have own interface display_banner
@@ -136,7 +136,7 @@ int from_backend_eof(void * /*frontend*/)
   return FALSE;
 }
 //---------------------------------------------------------------------------
-int get_userpass_input(prompts_t * p, const unsigned char * /*in*/, int /*inlen*/)
+int get_userpass_input(prompts_t * p, bufchain * DebugUsedArg(input))
 {
   DebugAssert(p != NULL);
   TSecureShell * SecureShell = reinterpret_cast<TSecureShell *>(p->frontend);
@@ -682,8 +682,7 @@ void SaveKey(TKeyType KeyType, const UnicodeString & FileName,
 void FreeKey(TPrivateKey * PrivateKey)
 {
   struct ssh2_userkey * Ssh2Key = reinterpret_cast<struct ssh2_userkey *>(PrivateKey);
-  Ssh2Key->alg->freekey(Ssh2Key->data);
-  sfree(Ssh2Key->comment);
+  ssh_key_free(Ssh2Key->key);
   sfree(Ssh2Key);
 }
 //---------------------------------------------------------------------------
@@ -698,9 +697,8 @@ RawByteString LoadPublicKey(const UnicodeString & FileName, UnicodeString & Algo
     int PublicKeyLen = 0;
     char * CommentStr = NULL;
     const char * ErrorStr = NULL;
-    unsigned char * PublicKeyPtr =
-      ssh2_userkey_loadpub(KeyFile, &AlgorithmStr, &PublicKeyLen, &CommentStr, &ErrorStr);
-    if (PublicKeyPtr == NULL)
+    strbuf * PublicKeyBuf = strbuf_new();
+    if (!ssh2_userkey_loadpub(KeyFile, &AlgorithmStr, BinarySink_UPCAST(PublicKeyBuf), &CommentStr, &ErrorStr))
     {
       UnicodeString Error = UnicodeString(AnsiString(ErrorStr));
       throw Exception(Error);
@@ -709,8 +707,8 @@ RawByteString LoadPublicKey(const UnicodeString & FileName, UnicodeString & Algo
     sfree(AlgorithmStr);
     Comment = UnicodeString(AnsiString(CommentStr));
     sfree(CommentStr);
-    Result = RawByteString(reinterpret_cast<char *>(PublicKeyPtr), PublicKeyLen);
-    free(PublicKeyPtr);
+    Result = RawByteString(reinterpret_cast<char *>(PublicKeyBuf->s), PublicKeyBuf->len);
+    strbuf_free(PublicKeyBuf);
   }
   __finally
   {
@@ -749,7 +747,7 @@ bool __fastcall HasGSSAPI(UnicodeString CustomPath)
         Ssh_gss_ctx ctx;
         memset(&ctx, 0, sizeof(ctx));
         has =
-          ((library->acquire_cred(library, &ctx) == SSH_GSS_OK) &&
+          ((library->acquire_cred(library, &ctx, NULL) == SSH_GSS_OK) &&
            (library->release_cred(library, &ctx) == SSH_GSS_OK)) ? 1 : 0;
       }
     }
@@ -771,7 +769,7 @@ static void __fastcall DoNormalizeFingerprint(UnicodeString & Fingerprint, Unico
 {
   const wchar_t NormalizedSeparator = L'-';
   const int MaxCount = 10;
-  const ssh_signkey * SignKeys[MaxCount];
+  const ssh_keyalg * SignKeys[MaxCount];
   int Count = LENOF(SignKeys);
   // We may use find_pubkey_alg, but it gets complicated with normalized fingerprint
   // as the names have different number of dashes
@@ -779,8 +777,8 @@ static void __fastcall DoNormalizeFingerprint(UnicodeString & Fingerprint, Unico
 
   for (int Index = 0; Index < Count; Index++)
   {
-    const ssh_signkey * SignKey = SignKeys[Index];
-    UnicodeString Name = UnicodeString(SignKey->name);
+    const ssh_keyalg * SignKey = SignKeys[Index];
+    UnicodeString Name = UnicodeString(SignKey->ssh_id);
     if (StartsStr(Name + L" ", Fingerprint))
     {
       int LenStart = Name.Length() + 1;
@@ -794,13 +792,13 @@ static void __fastcall DoNormalizeFingerprint(UnicodeString & Fingerprint, Unico
         Fingerprint.Delete(LenStart + 1, Space - LenStart);
         // noop for SHA256 fingerprints
         Fingerprint = ReplaceChar(Fingerprint, L':', NormalizedSeparator);
-        KeyType = UnicodeString(SignKey->keytype);
+        KeyType = UnicodeString(SignKey->cache_id);
         return;
       }
     }
     else if (StartsStr(Name + NormalizedSeparator, Fingerprint))
     {
-      KeyType = UnicodeString(SignKey->keytype);
+      KeyType = UnicodeString(SignKey->cache_id);
       return;
     }
   }
@@ -845,16 +843,15 @@ void __fastcall DllHijackingProtection()
   dll_hijacking_protection();
 }
 //---------------------------------------------------------------------------
-UnicodeString __fastcall ParseOpenSshPubLine(const UnicodeString & Line, const struct ssh_signkey *& Algorithm)
+UnicodeString __fastcall ParseOpenSshPubLine(const UnicodeString & Line, const struct ssh_keyalg *& Algorithm)
 {
   UTF8String UtfLine = UTF8String(Line);
   char * AlgorithmName = NULL;
-  int PubBlobLen = 0;
   char * CommentPtr = NULL;
   const char * ErrorStr = NULL;
-  unsigned char * PubBlob = openssh_loadpub_line(UtfLine.c_str(), &AlgorithmName, &PubBlobLen, &CommentPtr, &ErrorStr);
+  strbuf * PubBlobBuf = strbuf_new();
   UnicodeString Result;
-  if (PubBlob == NULL)
+  if (!openssh_loadpub_line(UtfLine.c_str(), &AlgorithmName, BinarySink_UPCAST(PubBlobBuf), &CommentPtr, &ErrorStr))
   {
     throw Exception(UnicodeString(ErrorStr));
   }
@@ -868,19 +865,20 @@ UnicodeString __fastcall ParseOpenSshPubLine(const UnicodeString & Line, const s
         throw Exception(FORMAT(L"Unknown public key algorithm \"%s\".", (AlgorithmName)));
       }
 
-      void * Key = Algorithm->newkey(Algorithm, reinterpret_cast<const char*>(PubBlob), PubBlobLen);
+      ptrlen PtrLen = { PubBlobBuf->s, PubBlobBuf->len };
+      ssh_key * Key = Algorithm->new_pub(Algorithm, PtrLen);
       if (Key == NULL)
       {
         throw Exception(L"Invalid public key.");
       }
-      char * FmtKey = Algorithm->fmtkey(Key);
+      char * FmtKey = Algorithm->cache_str(Key);
       Result = UnicodeString(FmtKey);
       sfree(FmtKey);
       Algorithm->freekey(Key);
     }
     __finally
     {
-      sfree(PubBlob);
+      strbuf_free(PubBlobBuf);
       sfree(AlgorithmName);
       sfree(CommentPtr);
     }
@@ -891,27 +889,27 @@ UnicodeString __fastcall ParseOpenSshPubLine(const UnicodeString & Line, const s
 UnicodeString __fastcall GetKeyTypeHuman(const UnicodeString & KeyType)
 {
   UnicodeString Result;
-  if (KeyType == ssh_dss.keytype)
+  if (KeyType == ssh_dss.cache_id)
   {
     Result = L"DSA";
   }
-  else if (KeyType == ssh_rsa.keytype)
+  else if (KeyType == ssh_rsa.cache_id)
   {
     Result = L"RSA";
   }
-  else if (KeyType == ssh_ecdsa_ed25519.keytype)
+  else if (KeyType == ssh_ecdsa_ed25519.cache_id)
   {
     Result = L"Ed25519";
   }
-  else if (KeyType == ssh_ecdsa_nistp256.keytype)
+  else if (KeyType == ssh_ecdsa_nistp256.cache_id)
   {
     Result = L"ECDSA/nistp256";
   }
-  else if (KeyType == ssh_ecdsa_nistp384.keytype)
+  else if (KeyType == ssh_ecdsa_nistp384.cache_id)
   {
     Result = L"ECDSA/nistp384";
   }
-  else if (KeyType == ssh_ecdsa_nistp521.keytype)
+  else if (KeyType == ssh_ecdsa_nistp521.cache_id)
   {
     Result = L"ECDSA/nistp521";
   }
@@ -968,14 +966,14 @@ TStrings * SshHostKeyList()
 {
   std::unique_ptr<TStrings> Result(new TStringList());
   const int MaxCount = 10;
-  const ssh_signkey * SignKeys[MaxCount];
+  const ssh_keyalg * SignKeys[MaxCount];
   int Count = LENOF(SignKeys);
   get_hostkey_algs(&Count, SignKeys);
 
   for (int Index = 0; Index < Count; Index++)
   {
-    const ssh_signkey * SignKey = SignKeys[Index];
-    UnicodeString Name = UnicodeString(SignKey->name);
+    const ssh_keyalg * SignKey = SignKeys[Index];
+    UnicodeString Name = UnicodeString(SignKey->ssh_id);
     Result->Add(Name);
   }
   return Result.release();

+ 2 - 0
source/core/PuttyIntf.h

@@ -20,6 +20,8 @@ extern "C"
 // Defined in misc.h - Conflicts with std::min/max
 #undef min
 #undef max
+// Defined in marshal.h - Conflicts with xml.xmldom.hpp
+#undef get_data
 }
 //---------------------------------------------------------------------------
 #endif

+ 1 - 1
source/core/PuttyTools.h

@@ -34,7 +34,7 @@ UnicodeString __fastcall Sha256(const char * Data, size_t Size);
 //---------------------------------------------------------------------------
 void __fastcall DllHijackingProtection();
 //---------------------------------------------------------------------------
-UnicodeString __fastcall ParseOpenSshPubLine(const UnicodeString & Line, const struct ssh_signkey *& Algorithm);
+UnicodeString __fastcall ParseOpenSshPubLine(const UnicodeString & Line, const struct ssh_keyalg *& Algorithm);
 //---------------------------------------------------------------------------
 UnicodeString __fastcall GetKeyTypeHuman(const UnicodeString & KeyType);
 //---------------------------------------------------------------------------

+ 20 - 8
source/core/SecureShell.cpp

@@ -1829,9 +1829,10 @@ bool __fastcall TSecureShell::EnumNetworkEvents(SOCKET Socket, WSANETWORKEVENTS
     }
   }
 
-  return
+  bool Result =
     FLAGSET(Events.lNetworkEvents, FD_READ) ||
     FLAGSET(Events.lNetworkEvents, FD_CLOSE);
+  return Result;
 }
 //---------------------------------------------------------------------------
 void __fastcall TSecureShell::HandleNetworkEvents(SOCKET Socket, WSANETWORKEVENTS & Events)
@@ -1898,19 +1899,31 @@ bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventR
       Handles = sresize(Handles, HandleCount + 1, HANDLE);
       Handles[HandleCount] = FSocketEvent;
       unsigned int Timeout = MSec;
-      if (toplevel_callback_pending())
-      {
-        Timeout = 0;
-      }
 
       unsigned int WaitResult;
       do
       {
         unsigned int TimeoutStep = std::min(GUIUpdateInterval, Timeout);
+        if (toplevel_callback_pending())
+        {
+          TimeoutStep = 0;
+        }
         Timeout -= TimeoutStep;
         WaitResult = WaitForMultipleObjects(HandleCount + 1, Handles, FALSE, TimeoutStep);
         FUI->ProcessGUI();
-      } while ((WaitResult == WAIT_TIMEOUT) && (Timeout > 0));
+        // run_toplevel_callbacks can cause processing of pending raw data, so:
+        // 1) Check for changes in our pending buffer - wait criteria in Receive()
+        int PrevDataLen = (-static_cast<int>(OutLen) + static_cast<int>(PendLen));
+        // 2) Changes in session state - wait criteria in Init()
+        bool PrevSessionState = get_ssh_state_session(FBackendHandle);
+        if (run_toplevel_callbacks() &&
+            (((-static_cast<int>(OutLen) + static_cast<int>(PendLen)) > PrevDataLen) ||
+             (PrevSessionState != get_ssh_state_session(FBackendHandle))))
+        {
+          // Note that we still may process new network event now
+          Result = true;
+        }
+      } while ((WaitResult == WAIT_TIMEOUT) && (Timeout > 0) && !Result);
 
       if (WaitResult < WAIT_OBJECT_0 + HandleCount)
       {
@@ -1974,7 +1987,6 @@ bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventR
       sfree(Handles);
     }
 
-    run_toplevel_callbacks();
 
     unsigned int TicksAfter = GetTickCount();
     // ticks wraps once in 49.7 days
@@ -2193,7 +2205,7 @@ void __fastcall TPasteKeyHandler::Paste(TObject * /*Sender*/, unsigned int & Ans
     }
     else
     {
-      const struct ssh_signkey * Algorithm;
+      const struct ssh_keyalg * Algorithm;
       try
       {
         UnicodeString Key = ParseOpenSshPubLine(ClipboardText, Algorithm);

+ 3 - 3
source/core/SessionData.cpp

@@ -4369,12 +4369,12 @@ void __fastcall TStoredSessionList::ImportFromKnownHosts(TStrings * Lines)
             }
           }
 
-          const struct ssh_signkey * Algorithm;
+          const struct ssh_keyalg * Algorithm;
           UnicodeString Key = ParseOpenSshPubLine(Line, Algorithm);
           UnicodeString KeyKey =
-            FORMAT(L"%s@%d:%s", (Algorithm->keytype, SessionData->PortNumber, HostNameStr));
+            FORMAT(L"%s@%d:%s", (Algorithm->cache_id, SessionData->PortNumber, HostNameStr));
           UnicodeString HostKey =
-            FORMAT(L"%s:%s=%s", (Algorithm->name, KeyKey, Key));
+            FORMAT(L"%s:%s=%s", (Algorithm->ssh_id, KeyKey, Key));
           UnicodeString HostKeyList = SessionData->HostKey;
           AddToList(HostKeyList, HostKey, L";");
           SessionData->HostKey = HostKeyList;

+ 2 - 1
source/putty/be_misc.c

@@ -64,7 +64,8 @@ void log_proxy_stderr(Plug plug, bufchain *buf, const void *vdata, int len)
     const char *data = (const char *)vdata;
     int pos = 0;
     int msglen;
-    char *nlpos, *msg, *fullmsg;
+    const char *nlpos;
+    char *msg, *fullmsg;
 
     /*
      * This helper function allows us to collect the data written to a

+ 75 - 35
source/putty/callback.c

@@ -14,7 +14,7 @@ struct callback {
     void *ctx;
 };
 
-struct callback *cbhead = NULL, *cbtail = NULL;
+struct callback *cbcurr = NULL, *cbhead = NULL, *cbtail = NULL;
 
 toplevel_callback_notify_fn_t notify_frontend = NULL;
 void *frontend = NULL;
@@ -28,7 +28,48 @@ void request_callback_notifications(toplevel_callback_notify_fn_t fn,
     MPEXT_PUTTY_SECTION_LEAVE;
 }
 
-void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx)
+static void run_idempotent_callback(void *ctx)
+{
+    struct IdempotentCallback *ic = (struct IdempotentCallback *)ctx;
+    ic->queued = FALSE;
+    ic->fn(ic->ctx);
+}
+
+void queue_idempotent_callback(struct IdempotentCallback *ic)
+{
+    if (ic->queued)
+        return;
+    ic->queued = TRUE;
+    queue_toplevel_callback(run_idempotent_callback, ic);
+}
+
+void delete_callbacks_for_context(void *ctx)
+{
+    struct callback *newhead, *newtail;
+
+    newhead = newtail = NULL;
+    while (cbhead) {
+        struct callback *cb = cbhead;
+        cbhead = cbhead->next;
+        if (cb->ctx == ctx ||
+            (cb->fn == run_idempotent_callback &&
+             ((struct IdempotentCallback *)cb->ctx)->ctx == ctx)) {
+            sfree(cb);
+        } else {
+            if (!newhead)
+                newhead = cb;
+            else
+                newtail->next = cb;
+
+            newtail = cb;
+        }
+    }
+
+    cbhead = newhead;
+    cbtail = newtail;
+}
+
+void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx)
 {
     struct callback *cb;
 
@@ -37,10 +78,18 @@ void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx)
     cb->fn = fn;
     cb->ctx = ctx;
 
-    /* If the front end has requested notification of pending
+    /*
+     * If the front end has requested notification of pending
      * callbacks, and we didn't already have one queued, let it know
-     * we do have one now. */
-    if (notify_frontend && !cbhead)
+     * we do have one now.
+     *
+     * If cbcurr is non-NULL, i.e. we are actually in the middle of
+     * executing a callback right now, then we count that as the queue
+     * already having been non-empty. That saves the front end getting
+     * a constant stream of needless re-notifications if the last
+     * callback keeps re-scheduling itself.
+     */
+    if (notify_frontend && !cbhead && !cbcurr)
         notify_frontend(frontend);
 
     if (cbtail)
@@ -52,47 +101,38 @@ void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx)
     MPEXT_PUTTY_SECTION_LEAVE;
 }
 
-void run_toplevel_callbacks(void)
+int run_toplevel_callbacks(void)
 {
+    int done_something = FALSE;
     MPEXT_PUTTY_SECTION_ENTER;
+
     if (cbhead) {
-        struct callback *cb = cbhead;
-        #ifdef MPEXT
-        toplevel_callback_fn_t fn = cb->fn;
-        void * ctx = cb->ctx;
-        cbhead = cb->next;
-        if (!cbhead)
-            cbtail = NULL;
-        cbhead = cb->next;
-        sfree(cb);
-        MPEXT_PUTTY_SECTION_LEAVE;
-        fn(ctx);
-        #else
         /*
-         * Careful ordering here. We call the function _before_
-         * advancing cbhead (though, of course, we must free cb
-         * _after_ advancing it). This means that if the very last
-         * callback schedules another callback, cbhead does not become
-         * NULL at any point, and so the frontend notification
-         * function won't be needlessly pestered.
+         * Transfer the head callback into cbcurr to indicate that
+         * it's being executed. Then operations which transform the
+         * queue, like delete_callbacks_for_context, can proceed as if
+         * it's not there.
          */
-        cb->fn(cb->ctx);
-        cbhead = cb->next;
-        sfree(cb);
+        cbcurr = cbhead;
+        cbhead = cbhead->next;
         if (!cbhead)
             cbtail = NULL;
-        #endif
-    }
-    #ifdef MPEXT
-    else
-    {
-      MPEXT_PUTTY_SECTION_LEAVE;
+
+        /*
+         * Now run the callback, and then clear it out of cbcurr.
+         */
+        cbcurr->fn(cbcurr->ctx);
+        sfree(cbcurr);
+        cbcurr = NULL;
+
+        done_something = TRUE;
     }
-    #endif
+    MPEXT_PUTTY_SECTION_LEAVE;
+    return done_something;
 }
 
 int toplevel_callback_pending(void)
 {
     // MP does not have to be guarded
-    return cbhead != NULL;
+    return cbcurr != NULL || cbhead != NULL;
 }

+ 29 - 120
source/putty/conf.c

@@ -469,179 +469,88 @@ void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
     conf_insert(conf, entry);
 }
 
-int conf_serialised_size(Conf *conf)
+void conf_serialise(BinarySink *bs, Conf *conf)
 {
     int i;
     struct conf_entry *entry;
-    int size = 0;
 
     for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
-	size += 4;   /* primary key */
-	switch (subkeytypes[entry->key.primary]) {
-	  case TYPE_INT:
-	    size += 4;
-	    break;
-	  case TYPE_STR:
-	    size += 1 + strlen(entry->key.secondary.s);
-	    break;
-	}
-	switch (valuetypes[entry->key.primary]) {
-	  case TYPE_INT:
-	    size += 4;
-	    break;
-	  case TYPE_STR:
-	    size += 1 + strlen(entry->value.u.stringval);
-	    break;
-	  case TYPE_FILENAME:
-	    size += filename_serialise(entry->value.u.fileval, NULL);
-	    break;
-	  case TYPE_FONT:
-	    size += fontspec_serialise(entry->value.u.fontval, NULL);
-	    break;
-	}
-    }
-
-    size += 4;			       /* terminator value */
-
-    return size;
-}
-
-void conf_serialise(Conf *conf, void *vdata)
-{
-    unsigned char *data = (unsigned char *)vdata;
-    int i, len;
-    struct conf_entry *entry;
-
-    for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
-	PUT_32BIT_MSB_FIRST(data, entry->key.primary);
-	data += 4;
+	put_uint32(bs, entry->key.primary);
 
 	switch (subkeytypes[entry->key.primary]) {
 	  case TYPE_INT:
-	    PUT_32BIT_MSB_FIRST(data, entry->key.secondary.i);
-	    data += 4;
+	    put_uint32(bs, entry->key.secondary.i);
 	    break;
 	  case TYPE_STR:
-	    len = strlen(entry->key.secondary.s);
-	    memcpy(data, entry->key.secondary.s, len);
-	    data += len;
-	    *data++ = 0;
+            put_asciz(bs, entry->key.secondary.s);
 	    break;
 	}
 	switch (valuetypes[entry->key.primary]) {
 	  case TYPE_INT:
-	    PUT_32BIT_MSB_FIRST(data, entry->value.u.intval);
-	    data += 4;
+	    put_uint32(bs, entry->value.u.intval);
 	    break;
 	  case TYPE_STR:
-	    len = strlen(entry->value.u.stringval);
-	    memcpy(data, entry->value.u.stringval, len);
-	    data += len;
-	    *data++ = 0;
+	    put_asciz(bs, entry->value.u.stringval);
 	    break;
 	  case TYPE_FILENAME:
-            data += filename_serialise(entry->value.u.fileval, data);
+            filename_serialise(bs, entry->value.u.fileval);
 	    break;
 	  case TYPE_FONT:
-            data += fontspec_serialise(entry->value.u.fontval, data);
+            fontspec_serialise(bs, entry->value.u.fontval);
 	    break;
 	}
     }
 
-    PUT_32BIT_MSB_FIRST(data, 0xFFFFFFFFU);
+    put_uint32(bs, 0xFFFFFFFFU);
 }
 
-int conf_deserialise(Conf *conf, void *vdata, int maxsize)
+int conf_deserialise(Conf *conf, BinarySource *src)
 {
-    unsigned char *data = (unsigned char *)vdata;
-    unsigned char *start = data;
     struct conf_entry *entry;
     unsigned primary;
-    int used;
-    unsigned char *zero;
 
-    while (maxsize >= 4) {
-	primary = GET_32BIT_MSB_FIRST(data);
-	data += 4, maxsize -= 4;
+    while (1) {
+        primary = get_uint32(src);
 
+        if (get_err(src))
+            return FALSE;
+        if (primary == 0xFFFFFFFFU)
+            return TRUE;
 	if (primary >= N_CONFIG_OPTIONS)
-	    break;
+	    return FALSE;
 
 	entry = snew(struct conf_entry);
 	entry->key.primary = primary;
 
 	switch (subkeytypes[entry->key.primary]) {
 	  case TYPE_INT:
-	    if (maxsize < 4) {
-		sfree(entry);
-		goto done;
-	    }
-	    entry->key.secondary.i = toint(GET_32BIT_MSB_FIRST(data));
-	    data += 4, maxsize -= 4;
+	    entry->key.secondary.i = toint(get_uint32(src));
 	    break;
 	  case TYPE_STR:
-	    zero = memchr(data, 0, maxsize);
-	    if (!zero) {
-		sfree(entry);
-		goto done;
-	    }
-	    entry->key.secondary.s = dupstr((char *)data);
-	    maxsize -= (zero + 1 - data);
-	    data = zero + 1;
+	    entry->key.secondary.s = dupstr(get_asciz(src));
 	    break;
 	}
 
 	switch (valuetypes[entry->key.primary]) {
 	  case TYPE_INT:
-	    if (maxsize < 4) {
-		if (subkeytypes[entry->key.primary] == TYPE_STR)
-		    sfree(entry->key.secondary.s);
-		sfree(entry);
-		goto done;
-	    }
-	    entry->value.u.intval = toint(GET_32BIT_MSB_FIRST(data));
-	    data += 4, maxsize -= 4;
+	    entry->value.u.intval = toint(get_uint32(src));
 	    break;
 	  case TYPE_STR:
-	    zero = memchr(data, 0, maxsize);
-	    if (!zero) {
-		if (subkeytypes[entry->key.primary] == TYPE_STR)
-		    sfree(entry->key.secondary.s);
-		sfree(entry);
-		goto done;
-	    }
-	    entry->value.u.stringval = dupstr((char *)data);
-	    maxsize -= (zero + 1 - data);
-	    data = zero + 1;
+	    entry->value.u.stringval = dupstr(get_asciz(src));
 	    break;
 	  case TYPE_FILENAME:
-            entry->value.u.fileval =
-                filename_deserialise(data, maxsize, &used);
-            if (!entry->value.u.fileval) {
-		if (subkeytypes[entry->key.primary] == TYPE_STR)
-		    sfree(entry->key.secondary.s);
-		sfree(entry);
-		goto done;
-	    }
-	    data += used;
-	    maxsize -= used;
+            entry->value.u.fileval = filename_deserialise(src);
 	    break;
 	  case TYPE_FONT:
-            entry->value.u.fontval =
-                fontspec_deserialise(data, maxsize, &used);
-            if (!entry->value.u.fontval) {
-		if (subkeytypes[entry->key.primary] == TYPE_STR)
-		    sfree(entry->key.secondary.s);
-		sfree(entry);
-		goto done;
-	    }
-	    data += used;
-	    maxsize -= used;
+            entry->value.u.fontval = fontspec_deserialise(src);
 	    break;
 	}
+
+        if (get_err(src)) {
+            free_entry(entry);
+            return FALSE;
+        }
+
 	conf_insert(conf, entry);
     }
-
-    done:
-    return (int)(data - start);
 }

+ 6 - 6
source/putty/cproxy.c

@@ -14,6 +14,7 @@
 #include "ssh.h" /* For MD5 support */
 #include "network.h"
 #include "proxy.h"
+#include "marshal.h"
 
 static void hmacmd5_chap(const unsigned char *challenge, int challen,
 			 const char *passwd, unsigned char *response)
@@ -36,13 +37,12 @@ static void hmacmd5_chap(const unsigned char *challenge, int challen,
     hmacmd5_free_context(hmacmd5_ctx);
 }
 
-void proxy_socks5_offerencryptedauth(char *command, int *len)
+void proxy_socks5_offerencryptedauth(BinarySink *bs)
 {
-    command[*len] = 0x03; /* CHAP */
-    (*len)++;
+    put_byte(bs, 0x03);              /* CHAP */
 }
 
-int proxy_socks5_handlechap (Proxy_Socket p)
+int proxy_socks5_handlechap (ProxySocket *p)
 {
 
     /* CHAP authentication reply format:
@@ -132,7 +132,7 @@ int proxy_socks5_handlechap (Proxy_Socket p)
 		hmacmd5_chap(data, p->chap_current_datalen,
 			     conf_get_str(p->conf, CONF_proxy_password),
 			     &outbuf[4]);
-		sk_write(p->sub_socket, (char *)outbuf, 20);
+		sk_write(p->sub_socket, outbuf, 20);
 	      break;
 	      case 0x11:
 	        /* Chose a protocol */
@@ -158,7 +158,7 @@ int proxy_socks5_handlechap (Proxy_Socket p)
     return 0;
 }
 
-int proxy_socks5_selectchap(Proxy_Socket p)
+int proxy_socks5_selectchap(ProxySocket *p)
 {
     char *username = conf_get_str(p->conf, CONF_proxy_username);
     char *password = conf_get_str(p->conf, CONF_proxy_password);

+ 80 - 0
source/putty/defs.h

@@ -0,0 +1,80 @@
+/*
+ * defs.h: initial definitions for PuTTY.
+ *
+ * The rule about this header file is that it can't depend on any
+ * other header file in this code base. This is where we define
+ * things, as much as we can, that other headers will want to refer
+ * to, such as opaque structure types and their associated typedefs,
+ * or macros that are used by other headers.
+ */
+
+#ifndef PUTTY_DEFS_H
+#define PUTTY_DEFS_H
+
+#include <stddef.h>
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+typedef struct conf_tag Conf;
+typedef struct backend_tag Backend;
+typedef struct terminal_tag Terminal;
+
+typedef struct Filename Filename;
+typedef struct FontSpec FontSpec;
+
+typedef struct bufchain_tag bufchain;
+
+typedef struct strbuf strbuf;
+
+struct RSAKey;
+
+#include <stdint.h>
+typedef uint32_t uint32;
+
+typedef struct BinarySink BinarySink;
+typedef struct BinarySource BinarySource;
+
+typedef struct SockAddr_tag *SockAddr;
+
+typedef struct Socket_vtable Socket_vtable;
+typedef struct Plug_vtable Plug_vtable;
+
+/* Note indirection: for historical reasons (it used to be closer to
+ * the OS socket type), the type that most code uses for a socket is
+ * 'Socket', not 'Socket *'. So an implementation of Socket or Plug
+ * has a 'const Socket *' field for the vtable pointer, and the
+ * 'Socket' type returned to client code is a pointer to _that_ in
+ * turn. */
+typedef const Socket_vtable **Socket;
+typedef const Plug_vtable **Plug;
+
+/*
+ * A small structure wrapping up a (pointer, length) pair so that it
+ * can be conveniently passed to or from a function.
+ */
+typedef struct ptrlen {
+    const void *ptr;
+    size_t len;
+} ptrlen;
+
+typedef struct logblank_t logblank_t;
+
+/* Do a compile-time type-check of 'to_check' (without evaluating it),
+ * as a side effect of returning the value 'to_return'. Note that
+ * although this macro double-*expands* to_return, it always
+ * *evaluates* exactly one copy of it, so it's side-effect safe. */
+#define TYPECHECK(to_check, to_return)                  \
+    (sizeof(to_check) ? (to_return) : (to_return))
+
+/* Return a pointer to the object of structure type 'type' whose field
+ * with name 'field' is pointed at by 'object'. */
+#define FROMFIELD(object, type, field)                                  \
+    TYPECHECK(object == &((type *)0)->field,                            \
+              ((type *)(((char *)(object)) - offsetof(type, field))))
+
+#endif /* PUTTY_DEFS_H */

+ 29 - 35
source/putty/errsock.c

@@ -10,37 +10,34 @@
 #include "putty.h"
 #include "network.h"
 
-typedef struct Socket_error_tag *Error_Socket;
-
-struct Socket_error_tag {
-    const struct socket_function_table *fn;
-    /* the above variable absolutely *must* be the first in this structure */
-
+typedef struct {
     char *error;
     Plug plug;
-};
+
+    const Socket_vtable *sockvt;
+} ErrorSocket;
 
 static Plug sk_error_plug(Socket s, Plug p)
 {
-    Error_Socket ps = (Error_Socket) s;
-    Plug ret = ps->plug;
+    ErrorSocket *es = FROMFIELD(s, ErrorSocket, sockvt);
+    Plug ret = es->plug;
     if (p)
-	ps->plug = p;
+	es->plug = p;
     return ret;
 }
 
 static void sk_error_close(Socket s)
 {
-    Error_Socket ps = (Error_Socket) s;
+    ErrorSocket *es = FROMFIELD(s, ErrorSocket, sockvt);
 
-    sfree(ps->error);
-    sfree(ps);
+    sfree(es->error);
+    sfree(es);
 }
 
 static const char *sk_error_socket_error(Socket s)
 {
-    Error_Socket ps = (Error_Socket) s;
-    return ps->error;
+    ErrorSocket *es = FROMFIELD(s, ErrorSocket, sockvt);
+    return es->error;
 }
 
 static char *sk_error_peer_info(Socket s)
@@ -48,26 +45,23 @@ static char *sk_error_peer_info(Socket s)
     return NULL;
 }
 
+static const Socket_vtable ErrorSocket_sockvt = {
+    sk_error_plug,
+    sk_error_close,
+    NULL /* write */,
+    NULL /* write_oob */,
+    NULL /* write_eof */,
+    NULL /* flush */,
+    NULL /* set_frozen */,
+    sk_error_socket_error,
+    sk_error_peer_info,
+};
+
 Socket new_error_socket(const char *errmsg, Plug plug)
 {
-    static const struct socket_function_table socket_fn_table = {
-	sk_error_plug,
-	sk_error_close,
-	NULL /* write */,
-	NULL /* write_oob */,
-	NULL /* write_eof */,
-	NULL /* flush */,
-	NULL /* set_frozen */,
-	sk_error_socket_error,
-	sk_error_peer_info,
-    };
-
-    Error_Socket ret;
-
-    ret = snew(struct Socket_error_tag);
-    ret->fn = &socket_fn_table;
-    ret->plug = plug;
-    ret->error = dupstr(errmsg);
-
-    return (Socket) ret;
+    ErrorSocket *es = snew(ErrorSocket);
+    es->sockvt = &ErrorSocket_sockvt;
+    es->plug = plug;
+    es->error = dupstr(errmsg);
+    return &es->sockvt;
 }

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 273 - 438
source/putty/import.c


+ 5 - 0
source/putty/int64.h

@@ -5,6 +5,8 @@
 #ifndef PUTTY_INT64_H
 #define PUTTY_INT64_H
 
+#include "defs.h"
+
 typedef struct {
     unsigned long hi, lo;
 } uint64;
@@ -21,4 +23,7 @@ uint64 uint64_shift_right(uint64 x, int shift);
 uint64 uint64_shift_left(uint64 x, int shift);
 uint64 uint64_from_decimal(char *str);
 
+void BinarySink_put_uint64(BinarySink *, uint64);
+uint64 BinarySource_get_uint64(BinarySource *);
+
 #endif

+ 2 - 5
source/putty/logging.c

@@ -164,7 +164,6 @@ void logfopen(void *handle)
 {
     struct LogContext *ctx = (struct LogContext *)handle;
     struct tm tm;
-    FILE *fp;
     int mode;
 
     /* Prevent repeat calls */
@@ -184,10 +183,8 @@ void logfopen(void *handle)
                    conf_get_str(ctx->conf, CONF_host),
                    conf_get_int(ctx->conf, CONF_port), &tm);
 
-    fp = f_open(ctx->currlogfilename, "r", FALSE);  /* file already present? */
-    if (fp) {
+    if (open_for_write_would_lose_data(ctx->currlogfilename)) {
 	int logxfovr = conf_get_int(ctx->conf, CONF_logxfovr);
-	fclose(fp);
 	if (logxfovr != LGXF_ASK) {
 	    mode = ((logxfovr == LGXF_OVR) ? 2 : 1);
 	} else
@@ -351,7 +348,7 @@ void log_packet(void *handle, int direction, int type,
 	    }
 	    dumpdata[10+2+3*(p%16)] = smalldata[0];
 	    dumpdata[10+2+3*(p%16)+1] = smalldata[1];
-	    dumpdata[10+1+3*16+2+(p%16)] = (isprint(c) ? c : '.');
+	    dumpdata[10+1+3*16+2+(p%16)] = (c >= 0x20 && c < 0x7F ? c : '.');
 	    output_pos = (p%16) + 1;
 	}
 

+ 236 - 0
source/putty/marshal.c

@@ -0,0 +1,236 @@
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "marshal.h"
+#include "misc.h"
+#include "int64.h"
+
+void BinarySink_put_data(BinarySink *bs, const void *data, size_t len)
+{
+    bs->write(bs, data, len);
+}
+
+void BinarySink_put_padding(BinarySink *bs, size_t len, unsigned char padbyte)
+{
+    char buf[16];
+    memset(buf, padbyte, sizeof(buf));
+    while (len > 0) {
+        size_t thislen = len < sizeof(buf) ? len : sizeof(buf);
+        bs->write(bs, buf, thislen);
+        len -= thislen;
+    }
+}
+
+void BinarySink_put_byte(BinarySink *bs, unsigned char val)
+{
+    bs->write(bs, &val, 1);
+}
+
+void BinarySink_put_bool(BinarySink *bs, int val)
+{
+    unsigned char cval = val ? 1 : 0;
+    bs->write(bs, &cval, 1);
+}
+
+void BinarySink_put_uint16(BinarySink *bs, unsigned long val)
+{
+    unsigned char data[2];
+    PUT_16BIT_MSB_FIRST(data, val);
+    bs->write(bs, data, sizeof(data));
+}
+
+void BinarySink_put_uint32(BinarySink *bs, unsigned long val)
+{
+    unsigned char data[4];
+    PUT_32BIT_MSB_FIRST(data, val);
+    bs->write(bs, data, sizeof(data));
+}
+
+void BinarySink_put_uint64(BinarySink *bs, uint64 val)
+{
+    BinarySink_put_uint32(bs, val.hi);
+    BinarySink_put_uint32(bs, val.lo);
+}
+
+void BinarySink_put_string(BinarySink *bs, const void *data, size_t len)
+{
+    /* Check that the string length fits in a uint32, without doing a
+     * potentially implementation-defined shift of more than 31 bits */
+    assert((len >> 31) < 2);
+
+    BinarySink_put_uint32(bs, len);
+    bs->write(bs, data, len);
+}
+
+void BinarySink_put_stringpl(BinarySink *bs, ptrlen pl)
+{
+    BinarySink_put_string(bs, pl.ptr, pl.len);
+}
+
+void BinarySink_put_stringz(BinarySink *bs, const char *str)
+{
+    BinarySink_put_string(bs, str, strlen(str));
+}
+
+void BinarySink_put_stringsb(BinarySink *bs, struct strbuf *buf)
+{
+    BinarySink_put_string(bs, buf->s, buf->len);
+    strbuf_free(buf);
+}
+
+void BinarySink_put_asciz(BinarySink *bs, const char *str)
+{
+    bs->write(bs, str, strlen(str) + 1);
+}
+
+int BinarySink_put_pstring(BinarySink *bs, const char *str)
+{
+    size_t len = strlen(str);
+    if (len > 255)
+        return FALSE; /* can't write a Pascal-style string this long */
+    BinarySink_put_byte(bs, len);
+    bs->write(bs, str, len);
+    return TRUE;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int BinarySource_data_avail(BinarySource *src, size_t wanted)
+{
+    if (src->err)
+        return FALSE;
+
+    if (wanted <= src->len - src->pos)
+        return TRUE;
+
+    src->err = BSE_OUT_OF_DATA;
+    return FALSE;
+}
+
+#define avail(wanted) BinarySource_data_avail(src, wanted)
+#define advance(dist) (src->pos += dist)
+#define here ((const void *)((const unsigned char *)src->data + src->pos))
+#define consume(dist)                           \
+    ((const void *)((const unsigned char *)src->data + \
+                    ((src->pos += dist) - dist)))
+
+ptrlen BinarySource_get_data(BinarySource *src, size_t wanted)
+{
+    if (!avail(wanted))
+        return make_ptrlen("", 0);
+
+    return make_ptrlen(consume(wanted), wanted);
+}
+
+unsigned char BinarySource_get_byte(BinarySource *src)
+{
+    const unsigned char *ucp;
+
+    if (!avail(1))
+        return 0;
+
+    ucp = consume(1);
+    return *ucp;
+}
+
+int BinarySource_get_bool(BinarySource *src)
+{
+    const unsigned char *ucp;
+
+    if (!avail(1))
+        return 0;
+
+    ucp = consume(1);
+    return *ucp != 0;
+}
+
+unsigned BinarySource_get_uint16(BinarySource *src)
+{
+    const unsigned char *ucp;
+
+    if (!avail(2))
+        return 0;
+
+    ucp = consume(2);
+    return GET_16BIT_MSB_FIRST(ucp);
+}
+
+unsigned long BinarySource_get_uint32(BinarySource *src)
+{
+    const unsigned char *ucp;
+
+    if (!avail(4))
+        return 0;
+
+    ucp = consume(4);
+    return GET_32BIT_MSB_FIRST(ucp);
+}
+
+uint64 BinarySource_get_uint64(BinarySource *src)
+{
+    const unsigned char *ucp;
+    uint64 toret;
+
+    if (!avail(8)) {
+        toret.hi = toret.lo = 0;
+        return toret;
+    }
+
+    ucp = consume(8);
+    toret.hi = GET_32BIT_MSB_FIRST(ucp);
+    toret.lo = GET_32BIT_MSB_FIRST(ucp + 4);
+    return toret;
+}
+
+ptrlen BinarySource_get_string(BinarySource *src)
+{
+    const unsigned char *ucp;
+    size_t len;
+
+    if (!avail(4))
+        return make_ptrlen("", 0);
+
+    ucp = consume(4);
+    len = GET_32BIT_MSB_FIRST(ucp);
+
+    if (!avail(len))
+        return make_ptrlen("", 0);
+
+    return make_ptrlen(consume(len), len);
+}
+
+const char *BinarySource_get_asciz(BinarySource *src)
+{
+    const char *start, *end;
+
+    if (src->err)
+        return "";
+
+    start = here;
+    end = memchr(start, '\0', src->len - src->pos);
+    if (!end) {
+        src->err = BSE_OUT_OF_DATA;
+        return "";
+    }
+
+    advance(end + 1 - start);
+    return start;
+}
+
+ptrlen BinarySource_get_pstring(BinarySource *src)
+{
+    const unsigned char *ucp;
+    size_t len;
+
+    if (!avail(1))
+        return make_ptrlen("", 0);
+
+    ucp = consume(1);
+    len = *ucp;
+
+    if (!avail(len))
+        return make_ptrlen("", 0);
+
+    return make_ptrlen(consume(len), len);
+}

+ 273 - 0
source/putty/marshal.h

@@ -0,0 +1,273 @@
+#ifndef PUTTY_MARSHAL_H
+#define PUTTY_MARSHAL_H
+
+#include "defs.h"
+
+/*
+ * A sort of 'abstract base class' or 'interface' or 'trait' which is
+ * the common feature of all types that want to accept data formatted
+ * using the SSH binary conventions of uint32, string, mpint etc.
+ */
+struct BinarySink {
+    void (*write)(BinarySink *sink, const void *data, size_t len);
+    BinarySink *binarysink_;
+};
+
+/*
+ * To define a structure type as a valid target for binary formatted
+ * data, put 'BinarySink_IMPLEMENTATION' in its declaration, and when
+ * an instance is set up, use 'BinarySink_INIT' to initialise the
+ * 'base class' state, providing a function pointer to be the
+ * implementation of the write() call above.
+ */
+#define BinarySink_IMPLEMENTATION BinarySink binarysink_[1]
+#define BinarySink_INIT(obj, writefn) \
+    ((obj)->binarysink_->write = (writefn), \
+     (obj)->binarysink_->binarysink_ = (obj)->binarysink_)
+
+/*
+ * The implementing type's write function will want to downcast its
+ * 'BinarySink *' parameter back to the more specific type. Also,
+ * sometimes you'll want to upcast a pointer to a particular
+ * implementing type into an abstract 'BinarySink *' to pass to
+ * generic subroutines not defined in this file. These macros do that
+ * job.
+ *
+ * Importantly, BinarySink_UPCAST can also be applied to a BinarySink
+ * * itself (and leaves it unchanged). That's achieved by a small
+ * piece of C trickery: implementing structures and the BinarySink
+ * structure itself both contain a field called binarysink_, but in
+ * implementing objects it's a BinarySink[1] whereas in the abstract
+ * type it's a 'BinarySink *' pointing back to the same structure,
+ * meaning that you can say 'foo->binarysink_' in either case and get
+ * a pointer type by different methods.
+ */
+#define BinarySink_DOWNCAST(object, type)                               \
+    TYPECHECK((object) == ((type *)0)->binarysink_,                     \
+              ((type *)(((char *)(object)) - offsetof(type, binarysink_))))
+#define BinarySink_UPCAST(object)                                       \
+    TYPECHECK((object)->binarysink_ == (BinarySink *)0,                 \
+              (object)->binarysink_)
+
+/*
+ * If you structure-copy an object that's implementing BinarySink,
+ * then that tricky self-pointer in its trait subobject will point to
+ * the wrong place. You could call BinarySink_INIT again, but this
+ * macro is terser and does all that's needed to fix up the copied
+ * object.
+ */
+#define BinarySink_COPIED(obj) \
+    ((obj)->binarysink_->binarysink_ = (obj)->binarysink_)
+
+/*
+ * The put_* macros are the main client to this system. Any structure
+ * which implements the BinarySink 'trait' is valid for use as the
+ * first parameter of any of these put_* macros.
+ */
+
+/* Basic big-endian integer types. uint64 is the structure type
+ * defined in int64.h, not the C99 built-in type. */
+#define put_byte(bs, val) \
+    BinarySink_put_byte(BinarySink_UPCAST(bs), val)
+#define put_uint16(bs, val) \
+    BinarySink_put_uint16(BinarySink_UPCAST(bs), val)
+#define put_uint32(bs, val) \
+    BinarySink_put_uint32(BinarySink_UPCAST(bs), val)
+#define put_uint64(bs, val) \
+    BinarySink_put_uint64(BinarySink_UPCAST(bs), val)
+
+/* SSH booleans, encoded as a single byte storing either 0 or 1. */
+#define put_bool(bs, val) \
+    BinarySink_put_bool(BinarySink_UPCAST(bs), val)
+
+/* SSH strings, with a leading uint32 length field. 'stringz' is a
+ * convenience function that takes an ordinary C zero-terminated
+ * string as input. 'stringsb' takes a strbuf * as input, and
+ * finalises it as a side effect (handy for multi-level marshalling in
+ * which you use these same functions to format an inner blob of data
+ * that then gets wrapped into a string container in an outer one). */
+#define put_string(bs, val, len) \
+    BinarySink_put_string(BinarySink_UPCAST(bs),val,len)
+#define put_stringpl(bs, ptrlen) \
+    BinarySink_put_stringpl(BinarySink_UPCAST(bs),ptrlen)
+#define put_stringz(bs, val) \
+    BinarySink_put_stringz(BinarySink_UPCAST(bs), val)
+#define put_stringsb(bs, val) \
+    BinarySink_put_stringsb(BinarySink_UPCAST(bs), val)
+
+/* Other string outputs: 'asciz' emits the string data directly into
+ * the output including the terminating \0, and 'pstring' emits the
+ * string in Pascal style with a leading _one_-byte length field.
+ * pstring can fail if the string is too long. */
+#define put_asciz(bs, val) \
+    BinarySink_put_asciz(BinarySink_UPCAST(bs), val)
+#define put_pstring(bs, val) \
+    BinarySink_put_pstring(BinarySink_UPCAST(bs), val)
+
+/* Multiprecision integers, in both the SSH-1 and SSH-2 formats. */
+#define put_mp_ssh1(bs, val) \
+    BinarySink_put_mp_ssh1(BinarySink_UPCAST(bs), val)
+#define put_mp_ssh2(bs, val) \
+    BinarySink_put_mp_ssh2(BinarySink_UPCAST(bs), val)
+
+/* Padding with a specified byte. */
+#define put_padding(bs, len, padbyte) \
+    BinarySink_put_padding(BinarySink_UPCAST(bs), len, padbyte)
+
+/* Fallback: just emit raw data bytes, using a syntax that matches the
+ * rest of these macros. */
+#define put_data(bs, val, len) \
+    BinarySink_put_data(BinarySink_UPCAST(bs), val, len)
+
+/*
+ * The underlying real C functions that implement most of those
+ * macros. Generally you won't want to call these directly, because
+ * they have such cumbersome names; you call the wrapper macros above
+ * instead.
+ *
+ * A few functions whose wrapper macros are defined above are actually
+ * declared in other headers, so as to guarantee that the
+ * declaration(s) of their other parameter type(s) are in scope.
+ */
+void BinarySink_put_data(BinarySink *, const void *data, size_t len);
+void BinarySink_put_padding(BinarySink *, size_t len, unsigned char padbyte);
+void BinarySink_put_byte(BinarySink *, unsigned char);
+void BinarySink_put_bool(BinarySink *, int);
+void BinarySink_put_uint16(BinarySink *, unsigned long);
+void BinarySink_put_uint32(BinarySink *, unsigned long);
+void BinarySink_put_string(BinarySink *, const void *data, size_t len);
+void BinarySink_put_stringpl(BinarySink *, ptrlen);
+void BinarySink_put_stringz(BinarySink *, const char *str);
+struct strbuf;
+void BinarySink_put_stringsb(BinarySink *, struct strbuf *);
+void BinarySink_put_asciz(BinarySink *, const char *str);
+int BinarySink_put_pstring(BinarySink *, const char *str);
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * A complementary trait structure for _un_-marshalling.
+ *
+ * This structure contains client-visible data fields rather than
+ * methods, because that seemed more useful than leaving it totally
+ * opaque. But it's still got the self-pointer system that will allow
+ * the set of get_* macros to target one of these itself or any other
+ * type that 'derives' from it. So, for example, an SSH packet
+ * structure can act as a BinarySource while also having additional
+ * fields like the packet type.
+ */
+typedef enum BinarySourceError {
+    BSE_NO_ERROR,
+    BSE_OUT_OF_DATA,
+    BSE_INVALID
+} BinarySourceError;
+struct BinarySource {
+    /*
+     * (data, len) is the data block being decoded. pos is the current
+     * position within the block.
+     */
+    const void *data;
+    size_t pos, len;
+
+    /*
+     * 'err' indicates whether a decoding error has happened at any
+     * point. Once this has been set to something other than
+     * BSE_NO_ERROR, it shouldn't be changed by any unmarshalling
+     * function. So you can safely do a long sequence of get_foo()
+     * operations and then test err just once at the end, rather than
+     * having to conditionalise every single get.
+     *
+     * The unmarshalling functions should always return some value,
+     * even if a decoding error occurs. Generally on error they'll
+     * return zero (if numeric) or the empty string (if string-based),
+     * or some other appropriate default value for more complicated
+     * types.
+     *
+     * If the usual return value is dynamically allocated (e.g. a
+     * Bignum, or a normal C 'char *' string), then the error value is
+     * also dynamic in the same way. So you have to free exactly the
+     * same set of things whether or not there was a decoding error,
+     * which simplifies exit paths - for example, you could call a big
+     * pile of get_foo functions, then put the actual handling of the
+     * results under 'if (!get_err(src))', and then free everything
+     * outside that if.
+     */
+    BinarySourceError err;
+
+    /*
+     * Self-pointer for the implicit derivation trick, same as
+     * BinarySink above.
+     */
+    BinarySource *binarysource_;
+};
+
+/*
+ * Implementation macros, similar to BinarySink.
+ */
+#define BinarySource_IMPLEMENTATION BinarySource binarysource_[1]
+#define BinarySource_INIT__(obj, data_, len_)    \
+    ((obj)->data = (data_),                             \
+     (obj)->len = (len_),                               \
+     (obj)->pos = 0,                                    \
+     (obj)->err = BSE_NO_ERROR,                         \
+     (obj)->binarysource_ = (obj))
+#define BinarySource_BARE_INIT(obj, data_, len_)                \
+    TYPECHECK(&(obj)->binarysource_ == (BinarySource **)0,      \
+              BinarySource_INIT__(obj, data_, len_))
+#define BinarySource_INIT(obj, data_, len_)                             \
+    TYPECHECK(&(obj)->binarysource_ == (BinarySource (*)[1])0,          \
+              BinarySource_INIT__(BinarySource_UPCAST(obj), data_, len_))
+#define BinarySource_DOWNCAST(object, type)                               \
+    TYPECHECK((object) == ((type *)0)->binarysource_,                     \
+              ((type *)(((char *)(object)) - offsetof(type, binarysource_))))
+#define BinarySource_UPCAST(object)                                       \
+    TYPECHECK((object)->binarysource_ == (BinarySource *)0,                 \
+              (object)->binarysource_)
+#define BinarySource_COPIED(obj) \
+    ((obj)->binarysource_->binarysource_ = (obj)->binarysource_)
+
+#define get_data(src, len) \
+    BinarySource_get_data(BinarySource_UPCAST(src), len)
+#define get_byte(src) \
+    BinarySource_get_byte(BinarySource_UPCAST(src))
+#define get_bool(src) \
+    BinarySource_get_bool(BinarySource_UPCAST(src))
+#define get_uint16(src) \
+    BinarySource_get_uint16(BinarySource_UPCAST(src))
+#define get_uint32(src) \
+    BinarySource_get_uint32(BinarySource_UPCAST(src))
+#define get_uint64(src) \
+    BinarySource_get_uint64(BinarySource_UPCAST(src))
+#define get_string(src) \
+    BinarySource_get_string(BinarySource_UPCAST(src))
+#define get_asciz(src) \
+    BinarySource_get_asciz(BinarySource_UPCAST(src))
+#define get_pstring(src) \
+    BinarySource_get_pstring(BinarySource_UPCAST(src))
+#define get_mp_ssh1(src) \
+    BinarySource_get_mp_ssh1(BinarySource_UPCAST(src))
+#define get_mp_ssh2(src) \
+    BinarySource_get_mp_ssh2(BinarySource_UPCAST(src))
+#define get_rsa_ssh1_pub(src, rsa, order) \
+    BinarySource_get_rsa_ssh1_pub(BinarySource_UPCAST(src), rsa, order)
+#define get_rsa_ssh1_priv(src, rsa) \
+    BinarySource_get_rsa_ssh1_priv(BinarySource_UPCAST(src), rsa)
+
+#define get_err(src) (BinarySource_UPCAST(src)->err)
+#define get_avail(src) (BinarySource_UPCAST(src)->len - \
+                       BinarySource_UPCAST(src)->pos)
+#define get_ptr(src)                                                    \
+    ((const void *)(                                                    \
+        (const unsigned char *)(BinarySource_UPCAST(src)->data) +       \
+        BinarySource_UPCAST(src)->pos))
+
+ptrlen BinarySource_get_data(BinarySource *, size_t);
+unsigned char BinarySource_get_byte(BinarySource *);
+int BinarySource_get_bool(BinarySource *);
+unsigned BinarySource_get_uint16(BinarySource *);
+unsigned long BinarySource_get_uint32(BinarySource *);
+ptrlen BinarySource_get_string(BinarySource *);
+const char *BinarySource_get_asciz(BinarySource *);
+ptrlen BinarySource_get_pstring(BinarySource *);
+
+#endif /* PUTTY_MARSHAL_H */

+ 105 - 45
source/putty/misc.c

@@ -360,6 +360,16 @@ int toint(unsigned u)
         return INT_MIN; /* fallback; should never occur on binary machines */
 }
 
+int string_length_for_printf(size_t s)
+{
+    /* Truncate absurdly long strings (should one show up) to fit
+     * within a positive 'int', which is what the "%.*s" format will
+     * expect. */
+    if (s > INT_MAX)
+        return INT_MAX;
+    return s;
+}
+
 /*
  * Do an sprintf(), but into a custom-allocated buffer.
  * 
@@ -466,47 +476,90 @@ char *dupprintf(const char *fmt, ...)
     return ret;
 }
 
-struct strbuf {
-    char *s;
-    int len, size;
+struct strbuf_impl {
+    int size;
+    struct strbuf visible;
 };
+
+#define STRBUF_SET_PTR(buf, ptr)                                \
+    ((buf)->visible.s = (ptr),                                  \
+     (buf)->visible.u = (unsigned char *)(buf)->visible.s)
+
+void *strbuf_append(strbuf *buf_o, size_t len)
+{
+    struct strbuf_impl *buf = FROMFIELD(buf_o, struct strbuf_impl, visible);
+    char *toret;
+    if (buf->size < buf->visible.len + len + 1) {
+        buf->size = (buf->visible.len + len + 1) * 5 / 4 + 512;
+        STRBUF_SET_PTR(buf, sresize(buf->visible.s, buf->size, char));
+    }
+    toret = buf->visible.s + buf->visible.len;
+    buf->visible.len += len;
+    buf->visible.s[buf->visible.len] = '\0';
+    return toret;
+}
+
+static void strbuf_BinarySink_write(
+    BinarySink *bs, const void *data, size_t len)
+{
+    strbuf *buf_o = BinarySink_DOWNCAST(bs, strbuf);
+    memcpy(strbuf_append(buf_o, len), data, len);
+}
+
 strbuf *strbuf_new(void)
 {
-    strbuf *buf = snew(strbuf);
-    buf->len = 0;
+    struct strbuf_impl *buf = snew(struct strbuf_impl);
+    BinarySink_INIT(&buf->visible, strbuf_BinarySink_write);
+    buf->visible.len = 0;
     buf->size = 512;
-    buf->s = snewn(buf->size, char);
-    *buf->s = '\0';
-    return buf;
+    STRBUF_SET_PTR(buf, snewn(buf->size, char));
+    *buf->visible.s = '\0';
+    return &buf->visible;
 }
-void strbuf_free(strbuf *buf)
+void strbuf_free(strbuf *buf_o)
 {
-    sfree(buf->s);
+    struct strbuf_impl *buf = FROMFIELD(buf_o, struct strbuf_impl, visible);
+    if (buf->visible.s) {
+        smemclr(buf->visible.s, buf->size);
+        sfree(buf->visible.s);
+    }
     sfree(buf);
 }
-char *strbuf_str(strbuf *buf)
+char *strbuf_to_str(strbuf *buf_o)
 {
-    return buf->s;
-}
-char *strbuf_to_str(strbuf *buf)
-{
-    char *ret = buf->s;
+    struct strbuf_impl *buf = FROMFIELD(buf_o, struct strbuf_impl, visible);
+    char *ret = buf->visible.s;
     sfree(buf);
     return ret;
 }
-void strbuf_catfv(strbuf *buf, const char *fmt, va_list ap)
+void strbuf_catfv(strbuf *buf_o, const char *fmt, va_list ap)
 {
-    buf->s = dupvprintf_inner(buf->s, buf->len, &buf->size, fmt, ap);
-    buf->len += strlen(buf->s + buf->len);
+    struct strbuf_impl *buf = FROMFIELD(buf_o, struct strbuf_impl, visible);
+    STRBUF_SET_PTR(buf, dupvprintf_inner(buf->visible.s, buf->visible.len,
+                                         &buf->size, fmt, ap));
+    buf->visible.len += strlen(buf->visible.s + buf->visible.len);
 }
-void strbuf_catf(strbuf *buf, const char *fmt, ...)
+void strbuf_catf(strbuf *buf_o, const char *fmt, ...)
 {
     va_list ap;
     va_start(ap, fmt);
-    strbuf_catfv(buf, fmt, ap);
+    strbuf_catfv(buf_o, fmt, ap);
     va_end(ap);
 }
 
+strbuf *strbuf_new_for_agent_query(void)
+{
+    strbuf *buf = strbuf_new();
+    put_uint32(buf, 0);                /* reserve space for length field */
+    return buf;
+}
+void strbuf_finalise_agent_query(strbuf *buf_o)
+{
+    struct strbuf_impl *buf = FROMFIELD(buf_o, struct strbuf_impl, visible);
+    assert(buf->visible.len >= 5);
+    PUT_32BIT_MSB_FIRST(buf->visible.u, buf->visible.len - 4);
+}
+
 /*
  * Read an entire line of text from a file. Return a buffer
  * malloced to be as big as necessary (caller must free).
@@ -754,6 +807,22 @@ void bufchain_fetch(bufchain *ch, void *data, int len)
     }
 }
 
+void bufchain_fetch_consume(bufchain *ch, void *data, int len)
+{
+    bufchain_fetch(ch, data, len);
+    bufchain_consume(ch, len);
+}
+
+int bufchain_try_fetch_consume(bufchain *ch, void *data, int len)
+{
+    if (ch->buffersize >= len) {
+        bufchain_fetch_consume(ch, data, len);
+        return TRUE;
+    } else {
+        return FALSE;
+    }
+}
+
 /* ----------------------------------------------------------------------
  * My own versions of malloc, realloc and free. Because I want
  * malloc and realloc to bomb out and exit the program if they run
@@ -1115,37 +1184,26 @@ int smemeq(const void *av, const void *bv, size_t len)
     return (0x100 - val) >> 8;
 }
 
-int match_ssh_id(int stringlen, const void *string, const char *id)
+ptrlen make_ptrlen(const void *ptr, size_t len)
 {
-    int idlen = strlen(id);
-    return (idlen == stringlen && !memcmp(string, id, idlen));
+    ptrlen pl;
+    pl.ptr = ptr;
+    pl.len = len;
+    return pl;
 }
 
-void *get_ssh_string(int *datalen, const void **data, int *stringlen)
+int ptrlen_eq_string(ptrlen pl, const char *str)
 {
-    void *ret;
-    unsigned int len;
-
-    if (*datalen < 4)
-        return NULL;
-    len = GET_32BIT_MSB_FIRST((const unsigned char *)*data);
-    if (*datalen - 4 < len)
-        return NULL;
-    ret = (void *)((const char *)*data + 4);
-    *datalen -= len + 4;
-    *data = (const char *)*data + len + 4;
-    *stringlen = len;
-    return ret;
+    size_t len = strlen(str);
+    return (pl.len == len && !memcmp(pl.ptr, str, len));
 }
 
-int get_ssh_uint32(int *datalen, const void **data, unsigned *ret)
+char *mkstr(ptrlen pl)
 {
-    if (*datalen < 4)
-        return FALSE;
-    *ret = GET_32BIT_MSB_FIRST((const unsigned char *)*data);
-    *datalen -= 4;
-    *data = (const char *)*data + 4;
-    return TRUE;
+    char *p = snewn(pl.len + 1, char);
+    memcpy(p, pl.ptr, pl.len);
+    p[pl.len] = '\0';
+    return p;
 }
 
 int strstartswith(const char *s, const char *t)
@@ -1186,6 +1244,8 @@ char *buildinfo(const char *newline)
     strbuf_catf(buf, "Visual Studio", newline);
 #if _MSC_VER == 1900
     strbuf_catf(buf, " 2015 / MSVC++ 14.0");
+#elif _MSC_VER == 1912
+    strbuf_catf(buf, " 2017 / MSVC++ 14.12");
 #elif _MSC_VER == 1800
     strbuf_catf(buf, " 2013 / MSVC++ 12.0");
 #elif _MSC_VER == 1700

+ 24 - 33
source/putty/misc.h

@@ -5,22 +5,14 @@
 #ifndef PUTTY_MISC_H
 #define PUTTY_MISC_H
 
+#include "defs.h"
 #include "puttymem.h"
+#include "marshal.h"
 
 #include <stdio.h>		       /* for FILE * */
 #include <stdarg.h>		       /* for va_list */
 #include <time.h>                      /* for struct tm */
 
-#ifndef FALSE
-#define FALSE 0
-#endif
-#ifndef TRUE
-#define TRUE 1
-#endif
-
-typedef struct Filename Filename;
-typedef struct FontSpec FontSpec;
-
 unsigned long parse_blocksize(const char *bs);
 char ctrlparse(char *s, char **next);
 
@@ -38,14 +30,24 @@ char *dupprintf(const char *fmt, ...)
     ;
 char *dupvprintf(const char *fmt, va_list ap);
 void burnstr(char *string);
-typedef struct strbuf strbuf;
+
+struct strbuf {
+    char *s;
+    unsigned char *u;
+    int len;
+    BinarySink_IMPLEMENTATION;
+    /* (also there's a surrounding implementation struct in misc.c) */
+};
 strbuf *strbuf_new(void);
 void strbuf_free(strbuf *buf);
-char *strbuf_str(strbuf *buf);         /* does not free buf */
+void *strbuf_append(strbuf *buf, size_t len);
 char *strbuf_to_str(strbuf *buf); /* does free buf, but you must free result */
 void strbuf_catf(strbuf *buf, const char *fmt, ...);
 void strbuf_catfv(strbuf *buf, const char *fmt, va_list ap);
 
+strbuf *strbuf_new_for_agent_query(void);
+void strbuf_finalise_agent_query(strbuf *buf);
+
 /* String-to-Unicode converters that auto-allocate the destination and
  * work around the rather deficient interface of mb_to_wc.
  *
@@ -70,10 +72,6 @@ struct bufchain_tag {
     struct bufchain_granule *head, *tail;
     int buffersize;		       /* current amount of buffered data */
 };
-#ifndef BUFCHAIN_TYPEDEF
-typedef struct bufchain_tag bufchain;  /* rest of declaration in misc.c */
-#define BUFCHAIN_TYPEDEF
-#endif
 
 void bufchain_init(bufchain *ch);
 void bufchain_clear(bufchain *ch);
@@ -82,11 +80,21 @@ void bufchain_add(bufchain *ch, const void *data, int len);
 void bufchain_prefix(bufchain *ch, void **data, int *len);
 void bufchain_consume(bufchain *ch, int len);
 void bufchain_fetch(bufchain *ch, void *data, int len);
+void bufchain_fetch_consume(bufchain *ch, void *data, int len);
+int bufchain_try_fetch_consume(bufchain *ch, void *data, int len);
 
 int validate_manual_hostkey(char *key);
 
 struct tm ltime(void);
 
+ptrlen make_ptrlen(const void *ptr, size_t len);
+int ptrlen_eq_string(ptrlen pl, const char *str);
+char *mkstr(ptrlen pl);
+int string_length_for_printf(size_t);
+/* Derive two printf arguments from a ptrlen, suitable for "%.*s" */
+#define PTRLEN_PRINTF(pl) \
+    string_length_for_printf((pl).len), (const char *)(pl).ptr
+
 /* Wipe sensitive data out of memory that's about to be freed. Simpler
  * than memset because we don't need the fill char parameter; also
  * attempts (by fiddly use of volatile) to inhibit the compiler from
@@ -101,23 +109,6 @@ void smemclr(void *b, size_t len);
  * by the 'eq' in the name. */
 int smemeq(const void *av, const void *bv, size_t len);
 
-/* Extracts an SSH-marshalled string from the start of *data. If
- * successful (*datalen is not too small), advances data/datalen past
- * the string and returns a pointer to the string itself and its
- * length in *stringlen. Otherwise does nothing and returns NULL.
- *
- * Like strchr, this function can discard const from its parameter.
- * Treat it as if it was a family of two functions, one returning a
- * non-const string given a non-const pointer, and one taking and
- * returning const. */
-void *get_ssh_string(int *datalen, const void **data, int *stringlen);
-/* Extracts an SSH uint32, similarly. Returns TRUE on success, and
- * leaves the extracted value in *ret. */
-int get_ssh_uint32(int *datalen, const void **data, unsigned *ret);
-/* Given a not-necessarily-zero-terminated string in (length,data)
- * form, check if it equals an ordinary C zero-terminated string. */
-int match_ssh_id(int stringlen, const void *string, const char *id);
-
 char *buildinfo(const char *newline);
 
 /*

+ 10 - 19
source/putty/network.h

@@ -13,26 +13,16 @@
 #ifndef PUTTY_NETWORK_H
 #define PUTTY_NETWORK_H
 
-#ifndef DONE_TYPEDEFS
-#define DONE_TYPEDEFS
-typedef struct conf_tag Conf;
-typedef struct backend_tag Backend;
-typedef struct terminal_tag Terminal;
-#endif
-
-typedef struct SockAddr_tag *SockAddr;
-/* pay attention to levels of indirection */
-typedef struct socket_function_table **Socket;
-typedef struct plug_function_table **Plug;
+#include "defs.h"
 
-struct socket_function_table {
+struct Socket_vtable {
     Plug(*plug) (Socket s, Plug p);
     /* use a different plug (return the old one) */
     /* if p is NULL, it doesn't change the plug */
     /* but it does return the one it's using */
     void (*close) (Socket s);
-    int (*write) (Socket s, const char *data, int len);
-    int (*write_oob) (Socket s, const char *data, int len);
+    int (*write) (Socket s, const void *data, int len);
+    int (*write_oob) (Socket s, const void *data, int len);
     void (*write_eof) (Socket s);
     void (*flush) (Socket s);
     void (*set_frozen) (Socket s, int is_frozen);
@@ -44,7 +34,7 @@ struct socket_function_table {
 typedef union { void *p; int i; } accept_ctx_t;
 typedef Socket (*accept_fn_t)(accept_ctx_t ctx, Plug plug);
 
-struct plug_function_table {
+struct Plug_vtable {
     void (*log)(Plug p, int type, SockAddr addr, int port,
 		const char *error_msg, int error_code);
     /*
@@ -227,6 +217,11 @@ char *get_hostname(void);
  */
 Socket new_error_socket(const char *errmsg, Plug plug);
 
+/*
+ * Trivial plug that does absolutely nothing. Found in nullplug.c.
+ */
+extern Plug nullplug;
+
 /* ----------------------------------------------------------------------
  * Functions defined outside the network code, which have to be
  * declared in this header file rather than the main putty.h because
@@ -239,10 +234,6 @@ Socket new_error_socket(const char *errmsg, Plug plug);
 void backend_socket_log(void *frontend, int type, SockAddr addr, int port,
                         const char *error_msg, int error_code, Conf *conf,
                         int session_started);
-#ifndef BUFCHAIN_TYPEDEF
-typedef struct bufchain_tag bufchain;  /* rest of declaration in misc.c */
-#define BUFCHAIN_TYPEDEF
-#endif
 void log_proxy_stderr(Plug plug, bufchain *buf, const void *vdata, int len);
 
 #endif

+ 42 - 0
source/putty/nullplug.c

@@ -0,0 +1,42 @@
+/*
+ * nullplug.c: provide a null implementation of the Plug vtable which
+ * ignores all calls. Occasionally useful in cases where we want to
+ * make a network connection just to see if it works, but not do
+ * anything with it afterwards except close it again.
+ */
+
+#include "putty.h"
+
+static void nullplug_socket_log(Plug plug, int type, SockAddr addr, int port,
+                                const char *error_msg, int error_code)
+{
+}
+
+static void nullplug_closing(Plug plug, const char *error_msg, int error_code,
+			     int calling_back)
+{
+}
+
+static void nullplug_receive(Plug plug, int urgent, char *data, int len)
+{
+}
+
+static void nullplug_sent(Plug plug, int bufsize)
+{
+}
+
+static const Plug_vtable nullplug_plugvt = {
+    nullplug_socket_log,
+    nullplug_closing,
+    nullplug_receive,
+    nullplug_sent,
+    NULL
+};
+
+static const Plug_vtable *nullplug_plugvt_ptr = &nullplug_plugvt;
+
+/*
+ * There's a singleton instance of nullplug, because it's not
+ * interesting enough to worry about making more than one of them.
+ */
+Plug nullplug = &nullplug_plugvt_ptr;

+ 17 - 14
source/putty/pageant.h

@@ -5,12 +5,11 @@
 #include <stdarg.h>
 
 /*
- * FIXME: it would be nice not to have this arbitrary limit. It's
- * currently needed because the Windows Pageant IPC system needs an
- * upper bound known to the client, but it's also reused as a basic
- * sanity check on incoming messages' length fields.
+ * Upper limit on length of any agent message. Used as a basic sanity
+ * check on messages' length fields, and used by the Windows Pageant
+ * client IPC to decide how large a file mapping to allocate.
  */
-#define AGENT_MAX_MSGLEN  8192
+#define AGENT_MAX_MSGLEN  262144
 
 typedef void (*pageant_logfn_t)(void *logctx, const char *fmt, va_list ap);
 
@@ -28,21 +27,26 @@ void pageant_init(void);
  * Returns a fully formatted message as output, *with* its initial
  * length field, and sets *outlen to the full size of that message.
  */
-void *pageant_handle_msg(const void *msg, int msglen, int *outlen,
-                         void *logctx, pageant_logfn_t logfn);
+void pageant_handle_msg(BinarySink *bs,
+                        const void *msg, int msglen,
+                        void *logctx, pageant_logfn_t logfn);
 
 /*
  * Construct a failure response. Useful for agent front ends which
  * suffer a problem before they even get to pageant_handle_msg.
+ *
+ * 'log_reason' is only used if logfn is not NULL.
  */
-void *pageant_failure_msg(int *outlen);
+void pageant_failure_msg(BinarySink *bs,
+                         const char *log_reason,
+                         void *logctx, pageant_logfn_t logfn);
 
 /*
  * Construct a list of public keys, just as the two LIST_IDENTITIES
  * requests would have returned them.
  */
-void *pageant_make_keylist1(int *length);
-void *pageant_make_keylist2(int *length);
+void pageant_make_keylist1(BinarySink *);
+void pageant_make_keylist2(BinarySink *);
 
 /*
  * Accessor functions for Pageant's internal key lists. Fetch the nth
@@ -74,13 +78,13 @@ void keylist_update(void);
 /*
  * Functions to establish a listening socket speaking the SSH agent
  * protocol. Call pageant_listener_new() to set up a state; then
- * create a socket using the returned pointer as a Plug; then call
+ * create a socket using the returned Plug; then call
  * pageant_listener_got_socket() to give the listening state its own
  * socket pointer. Also, provide a logging function later if you want
  * to.
  */
 struct pageant_listen_state;
-struct pageant_listen_state *pageant_listener_new(void);
+struct pageant_listen_state *pageant_listener_new(Plug *plug);
 void pageant_listener_got_socket(struct pageant_listen_state *pl, Socket sock);
 void pageant_listener_set_logfn(struct pageant_listen_state *pl,
                                 void *logctx, pageant_logfn_t logfn);
@@ -125,8 +129,7 @@ struct pageant_pubkey {
     /* Everything needed to identify a public key found by
      * pageant_enum_keys and pass it back to the agent or other code
      * later */
-    void *blob;
-    int bloblen;
+    strbuf *blob;
     char *comment;
     int ssh_version;
 };

+ 1 - 1
source/putty/pgssapi.c

@@ -43,7 +43,7 @@ static const gss_OID_desc oids[] = {
     {6, (void *)"\x2b\x06\x01\x05\x06\x02"},
     /* corresponding to an object-identifier value of
      * {iso(1) org(3) dod(6) internet(1) security(5)
-     * nametypes(6) gss-host-based-services(2)).  The constant
+     * nametypes(6) gss-host-based-services(2))}.  The constant
      * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point
      * to that gss_OID_desc.  This is a deprecated OID value, and
      * implementations wishing to support hostbased-service names

+ 37 - 0
source/putty/pgssapi.h

@@ -58,6 +58,7 @@ typedef void * gss_name_t;
 typedef void * gss_cred_id_t;
 
 typedef OM_uint32 gss_qop_t;
+typedef int gss_cred_usage_t;
 
 /* Flag bits for context-level services. */
 
@@ -76,6 +77,13 @@ typedef OM_uint32 gss_qop_t;
 #define GSS_C_INITIATE 1
 #define GSS_C_ACCEPT   2
 
+/*-
+ * RFC 2744 Page 86
+ * Expiration time of 2^32-1 seconds means infinite lifetime for a
+ * credential or security context
+ */
+#define GSS_C_INDEFINITE 0xfffffffful
+
 /* Status code types for gss_display_status */
 #define GSS_C_GSS_CODE  1
 #define GSS_C_MECH_CODE 2
@@ -256,6 +264,13 @@ typedef OM_uint32 (GSS_CC *t_gss_get_mic)
              const gss_buffer_t             /*message_buffer*/,
              gss_buffer_t                   /*msg_token*/);
 
+typedef OM_uint32 (GSS_CC *t_gss_verify_mic)
+            (OM_uint32                    * /*minor_status*/,
+             const gss_ctx_id_t             /*context_handle*/,
+             const gss_buffer_t             /*message_buffer*/,
+             const gss_buffer_t             /*msg_token*/,
+             gss_qop_t                    * /*qop_state*/);
+
 typedef OM_uint32 (GSS_CC *t_gss_display_status)
             (OM_uint32                   * /*minor_status*/,
              OM_uint32                     /*status_value*/,
@@ -280,15 +295,37 @@ typedef OM_uint32 (GSS_CC *t_gss_release_buffer)
             (OM_uint32                   * /*minor_status*/,
              gss_buffer_t                  /*buffer*/);
 
+typedef OM_uint32 (GSS_CC *t_gss_acquire_cred)
+            (OM_uint32                    * /*minor_status*/,
+             const gss_name_t               /*desired_name*/,
+             OM_uint32                      /*time_req*/,
+             const gss_OID_set              /*desired_mechs*/,
+             gss_cred_usage_t               /*cred_usage*/,
+             gss_cred_id_t                * /*output_cred_handle*/,
+             gss_OID_set                  * /*actual_mechs*/,
+             OM_uint32                    * /*time_rec*/);
+
+typedef OM_uint32 (GSS_CC *t_gss_inquire_cred_by_mech)
+            (OM_uint32                    * /*minor_status*/,
+             const gss_cred_id_t            /*cred_handle*/,
+             const gss_OID                  /*mech_type*/,
+             gss_name_t                   * /*name*/,
+             OM_uint32                    * /*initiator_lifetime*/,
+             OM_uint32                    * /*acceptor_lifetime*/,
+             gss_cred_usage_t             * /*cred_usage*/);
+
 struct gssapi_functions {
     t_gss_delete_sec_context delete_sec_context;
     t_gss_display_status display_status;
     t_gss_get_mic get_mic;
+    t_gss_verify_mic verify_mic;
     t_gss_import_name import_name;
     t_gss_init_sec_context init_sec_context;
     t_gss_release_buffer release_buffer;
     t_gss_release_cred release_cred;
     t_gss_release_name release_name;
+    t_gss_acquire_cred acquire_cred;
+    t_gss_inquire_cred_by_mech inquire_cred_by_mech;
 };
 
 #endif /* NO_GSSAPI */

+ 289 - 286
source/putty/portfwd.c

@@ -2,35 +2,33 @@
  * SSH port forwarding.
  */
 
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 
 #include "putty.h"
 #include "ssh.h"
 
-#ifndef FALSE
-#define FALSE 0
-#endif
-#ifndef TRUE
-#define TRUE 1
-#endif
+/*
+ * Enumeration of values that live in the 'socks_state' field of
+ * struct PortForwarding.
+ */
+typedef enum {
+    SOCKS_NONE, /* direct connection (no SOCKS, or SOCKS already done) */
+    SOCKS_INITIAL,       /* don't know if we're SOCKS 4 or 5 yet */
+    SOCKS_4,             /* expect a SOCKS 4 (or 4A) connection message */
+    SOCKS_5_INITIAL,     /* expect a SOCKS 5 preliminary message */
+    SOCKS_5_CONNECT      /* expect a SOCKS 5 connection message */
+} SocksState;
 
 struct PortForwarding {
-    const struct plug_function_table *fn;
-    /* the above variable absolutely *must* be the first in this structure */
     struct ssh_channel *c;        /* channel structure held by ssh.c */
     void *backhandle;		       /* instance of SSH backend itself */
     /* Note that backhandle need not be filled in if c is non-NULL */
     Socket s;
     int throttled, throttle_override;
     int ready;
-    /*
-     * `dynamic' does double duty. It's set to 0 for an ordinary
-     * forwarded port, and nonzero for SOCKS-style dynamic port
-     * forwarding; but the nonzero values are also a state machine
-     * tracking where the SOCKS exchange has got to.
-     */
-    int dynamic;
+    SocksState socks_state;
     /*
      * `hostname' and `port' are the real hostname and port, once
      * we know what we're connecting to.
@@ -38,36 +36,28 @@ struct PortForwarding {
     char *hostname;
     int port;
     /*
-     * `socksbuf' is the buffer we use to accumulate a SOCKS request.
+     * `socksbuf' is the buffer we use to accumulate the initial SOCKS
+     * segment of the incoming data, plus anything after that that we
+     * receive before we're ready to send data to the SSH server.
      */
-    char *socksbuf;
-    int sockslen, sockssize;
-    /*
-     * When doing dynamic port forwarding, we can receive
-     * connection data before we are actually able to send it; so
-     * we may have to temporarily hold some in a dynamically
-     * allocated buffer here.
-     */
-    void *buffer;
-    int buflen;
+    strbuf *socksbuf;
+    size_t socksbuf_consumed;
+
+    const Plug_vtable *plugvt;
 };
 
 struct PortListener {
-    const struct plug_function_table *fn;
-    /* the above variable absolutely *must* be the first in this structure */
     void *backhandle;		       /* instance of SSH backend itself */
     Socket s;
-    /*
-     * `dynamic' is set to 0 for an ordinary forwarded port, and
-     * nonzero for SOCKS-style dynamic port forwarding.
-     */
-    int dynamic;
+    int is_dynamic;
     /*
      * `hostname' and `port' are the real hostname and port, for
      * ordinary forwardings.
      */
     char *hostname;
     int port;
+
+    const Plug_vtable *plugvt;
 };
 
 static struct PortForwarding *new_portfwd_state(void)
@@ -75,8 +65,6 @@ static struct PortForwarding *new_portfwd_state(void)
     struct PortForwarding *pf = snew(struct PortForwarding);
     pf->hostname = NULL;
     pf->socksbuf = NULL;
-    pf->sockslen = pf->sockssize = 0;
-    pf->buffer = NULL;
     return pf;
 }
 
@@ -85,8 +73,8 @@ static void free_portfwd_state(struct PortForwarding *pf)
     if (!pf)
         return;
     sfree(pf->hostname);
-    sfree(pf->socksbuf);
-    sfree(pf->buffer);
+    if (pf->socksbuf)
+        strbuf_free(pf->socksbuf);
     sfree(pf);
 }
 
@@ -120,7 +108,7 @@ static void pfl_log(Plug plug, int type, SockAddr addr, int port,
 static void pfd_closing(Plug plug, const char *error_msg, int error_code,
 			int calling_back)
 {
-    struct PortForwarding *pf = (struct PortForwarding *) plug;
+    struct PortForwarding *pf = FROMFIELD(plug, struct PortForwarding, plugvt);
 
     if (error_msg) {
         /*
@@ -169,207 +157,238 @@ static void wrap_send_port_open(void *channel, const char *hostname, int port,
     sfree(description);
 }
 
+static char *ipv4_to_string(unsigned ipv4)
+{
+    return dupprintf("%u.%u.%u.%u",
+                     (ipv4 >> 24) & 0xFF, (ipv4 >> 16) & 0xFF,
+                     (ipv4 >>  8) & 0xFF, (ipv4      ) & 0xFF);
+}
+
+static char *ipv6_to_string(ptrlen ipv6)
+{
+    const unsigned char *addr = ipv6.ptr;
+    assert(ipv6.len == 16);
+    return dupprintf("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
+                     (unsigned)GET_16BIT_MSB_FIRST(addr + 0),
+                     (unsigned)GET_16BIT_MSB_FIRST(addr + 2),
+                     (unsigned)GET_16BIT_MSB_FIRST(addr + 4),
+                     (unsigned)GET_16BIT_MSB_FIRST(addr + 6),
+                     (unsigned)GET_16BIT_MSB_FIRST(addr + 8),
+                     (unsigned)GET_16BIT_MSB_FIRST(addr + 10),
+                     (unsigned)GET_16BIT_MSB_FIRST(addr + 12),
+                     (unsigned)GET_16BIT_MSB_FIRST(addr + 14));
+}
+
 static void pfd_receive(Plug plug, int urgent, char *data, int len)
 {
-    struct PortForwarding *pf = (struct PortForwarding *) plug;
-    if (pf->dynamic) {
-	while (len--) {
-	    if (pf->sockslen >= pf->sockssize) {
-                pf->sockssize = pf->sockslen * 5 / 4 + 256;
-                pf->socksbuf = sresize(pf->socksbuf, pf->sockssize, char);
-	    }
-	    pf->socksbuf[pf->sockslen++] = *data++;
-
-	    /*
-	     * Now check what's in the buffer to see if it's a
-	     * valid and complete message in the SOCKS exchange.
-	     */
-	    if ((pf->dynamic == 1 || (pf->dynamic >> 12) == 4) &&
-		pf->socksbuf[0] == 4) {
-		/*
-		 * SOCKS 4.
-		 */
-		if (pf->dynamic == 1)
-		    pf->dynamic = 0x4000;
-		if (pf->sockslen < 2)
-                    continue;        /* don't have command code yet */
-		if (pf->socksbuf[1] != 1) {
-		    /* Not CONNECT. */
-		    /* Send back a SOCKS 4 error before closing. */
-		    char data[8];
-		    memset(data, 0, sizeof(data));
-		    data[1] = 91;      /* generic `request rejected' */
-		    sk_write(pf->s, data, 8);
-		    pfd_close(pf);
-		    return;
-		}
-		if (pf->sockslen <= 8)
-                    continue;      /* haven't started user/hostname */
-		if (pf->socksbuf[pf->sockslen-1] != 0)
-		    continue;	       /* haven't _finished_ user/hostname */
-		/*
-		 * Now we have a full SOCKS 4 request. Check it to
-		 * see if it's a SOCKS 4A request.
-		 */
-		if (pf->socksbuf[4] == 0 && pf->socksbuf[5] == 0 &&
-		    pf->socksbuf[6] == 0 && pf->socksbuf[7] != 0) {
-		    /*
-		     * It's SOCKS 4A. So if we haven't yet
-		     * collected the host name, we should continue
-		     * waiting for data in order to do so; if we
-		     * have, we can go ahead.
-		     */
-		    int len;
-		    if (pf->dynamic == 0x4000) {
-			pf->dynamic = 0x4001;
-			pf->sockslen = 8; /* reset buffer to overwrite name */
-			continue;
-		    }
-		    pf->socksbuf[0] = 0;   /* reply version code */
-		    pf->socksbuf[1] = 90;   /* request granted */
-		    sk_write(pf->s, pf->socksbuf, 8);
-		    len = pf->sockslen - 8;
-		    pf->port = GET_16BIT_MSB_FIRST(pf->socksbuf+2);
-                    pf->hostname = snewn(len+1, char);
-                    pf->hostname[len] = '\0';
-		    memcpy(pf->hostname, pf->socksbuf + 8, len);
-		    goto connect;
-		} else {
-		    /*
-		     * It's SOCKS 4, which means we should format
-		     * the IP address into the hostname string and
-		     * then just go.
-		     */
-		    pf->socksbuf[0] = 0;   /* reply version code */
-		    pf->socksbuf[1] = 90;   /* request granted */
-		    sk_write(pf->s, pf->socksbuf, 8);
-		    pf->port = GET_16BIT_MSB_FIRST(pf->socksbuf+2);
-		    pf->hostname = dupprintf("%d.%d.%d.%d",
-                                             (unsigned char)pf->socksbuf[4],
-                                             (unsigned char)pf->socksbuf[5],
-                                             (unsigned char)pf->socksbuf[6],
-                                             (unsigned char)pf->socksbuf[7]);
-		    goto connect;
-		}
-	    }
-
-	    if ((pf->dynamic == 1 || (pf->dynamic >> 12) == 5) &&
-		pf->socksbuf[0] == 5) {
-		/*
-		 * SOCKS 5.
-		 */
-		if (pf->dynamic == 1)
-		    pf->dynamic = 0x5000;
-
-		if (pf->dynamic == 0x5000) {
-		    int i, method;
-		    char data[2];
-		    /*
-		     * We're receiving a set of method identifiers.
-		     */
-		    if (pf->sockslen < 2)
-                        continue;      /* no method count yet */
-		    if (pf->sockslen < 2 + (unsigned char)pf->socksbuf[1])
-			continue;      /* no methods yet */
-		    method = 0xFF;     /* invalid */
-		    for (i = 0; i < (unsigned char)pf->socksbuf[1]; i++)
-			if (pf->socksbuf[2+i] == 0) {
-			    method = 0;/* no auth */
-			    break;
-			}
-		    data[0] = 5;
-		    data[1] = method;
-		    sk_write(pf->s, data, 2);
-		    pf->dynamic = 0x5001;
-		    pf->sockslen = 0;      /* re-empty the buffer */
-		    continue;
-		}
-
-		if (pf->dynamic == 0x5001) {
-		    /*
-		     * We're receiving a SOCKS request.
-		     */
-		    unsigned char reply[10]; /* SOCKS5 atyp=1 reply */
-		    int atype, alen = 0;
-
-		    /*
-		     * Pre-fill reply packet.
-		     * In all cases, we set BND.{HOST,ADDR} to 0.0.0.0:0
-		     * (atyp=1) in the reply; if we succeed, we don't know
-		     * the right answers, and if we fail, they should be
-		     * ignored.
-		     */
-		    memset(reply, 0, lenof(reply));
-		    reply[0] = 5; /* VER */
-		    reply[3] = 1; /* ATYP = 1 (IPv4, 0.0.0.0:0) */
-
-		    if (pf->sockslen < 6) continue;
-		    atype = (unsigned char)pf->socksbuf[3];
-		    if (atype == 1)    /* IPv4 address */
-			alen = 4;
-		    if (atype == 4)    /* IPv6 address */
-			alen = 16;
-		    if (atype == 3)    /* domain name has leading length */
-			alen = 1 + (unsigned char)pf->socksbuf[4];
-		    if (pf->sockslen < 6 + alen) continue;
-		    if (pf->socksbuf[1] != 1 || pf->socksbuf[2] != 0) {
-			/* Not CONNECT or reserved field nonzero - error */
-			reply[1] = 1;	/* generic failure */
-			sk_write(pf->s, (char *) reply, lenof(reply));
-			pfd_close(pf);
-			return;
-		    }
-		    /*
-		     * Now we have a viable connect request. Switch
-		     * on atype.
-		     */
-		    pf->port = GET_16BIT_MSB_FIRST(pf->socksbuf+4+alen);
-		    if (atype == 1) {
-			/* REP=0 (success) already */
-			sk_write(pf->s, (char *) reply, lenof(reply));
-			pf->hostname = dupprintf("%d.%d.%d.%d",
-                                                 (unsigned char)pf->socksbuf[4],
-                                                 (unsigned char)pf->socksbuf[5],
-                                                 (unsigned char)pf->socksbuf[6],
-                                                 (unsigned char)pf->socksbuf[7]);
-			goto connect;
-		    } else if (atype == 3) {
-			/* REP=0 (success) already */
-			sk_write(pf->s, (char *) reply, lenof(reply));
-                        pf->hostname = snewn(alen, char);
-			pf->hostname[alen-1] = '\0';
-			memcpy(pf->hostname, pf->socksbuf + 5, alen-1);
-			goto connect;
-		    } else {
-			/*
-			 * Unknown address type. (FIXME: support IPv6!)
-			 */
-			reply[1] = 8;	/* atype not supported */
-			sk_write(pf->s, (char *) reply, lenof(reply));
-			pfd_close(pf);
-			return;
-		    }
-		}
-	    }
-
-	    /*
-	     * If we get here without either having done `continue'
-	     * or `goto connect', it must be because there is no
-	     * sensible interpretation of what's in our buffer. So
-	     * close the connection rudely.
-	     */
-	    pfd_close(pf);
-            break;
-	}
-	return;
+    struct PortForwarding *pf = FROMFIELD(plug, struct PortForwarding, plugvt);
+
+    if (len == 0)
+        return;
+
+    if (pf->socks_state != SOCKS_NONE) {
+        BinarySource src[1];
+
+        /*
+         * Store all the data we've got in socksbuf.
+         */
+        put_data(pf->socksbuf, data, len);
+
+        /*
+         * Check the start of socksbuf to see if it's a valid and
+         * complete message in the SOCKS exchange.
+         */
+
+        if (pf->socks_state == SOCKS_INITIAL) {
+            /* Preliminary: check the first byte of the data (which we
+             * _must_ have by now) to find out which SOCKS major
+             * version we're speaking. */
+            switch (pf->socksbuf->u[0]) {
+              case 4:
+                pf->socks_state = SOCKS_4;
+                break;
+              case 5:
+                pf->socks_state = SOCKS_5_INITIAL;
+                break;
+              default:
+                pfd_close(pf);         /* unrecognised version */
+                return;
+            }
+        }
+
+        BinarySource_BARE_INIT(src, pf->socksbuf->u, pf->socksbuf->len);
+        get_data(src, pf->socksbuf_consumed);
+
+        while (pf->socks_state != SOCKS_NONE) {
+            unsigned socks_version, message_type, reserved_byte;
+            unsigned reply_code, port, ipv4, method;
+            ptrlen methods;
+            const char *socks4_hostname;
+            strbuf *output;
+
+            switch (pf->socks_state) {
+              case SOCKS_INITIAL:
+              case SOCKS_NONE:
+                assert(0 && "These case values cannot appear");
+
+              case SOCKS_4:
+                /* SOCKS 4/4A connect message */
+                socks_version = get_byte(src);
+                message_type = get_byte(src);
+
+                if (get_err(src) == BSE_OUT_OF_DATA)
+                    return;
+                if (socks_version == 4 && message_type == 1) {
+                    /* CONNECT message */
+                    int name_based = FALSE;
+
+                    port = get_uint16(src);
+                    ipv4 = get_uint32(src);
+                    if (ipv4 > 0x00000000 && ipv4 < 0x00000100) {
+                        /*
+                         * Addresses in this range indicate the SOCKS 4A
+                         * extension to specify a hostname, which comes
+                         * after the username.
+                         */
+                        name_based = TRUE;
+                    }
+                    get_asciz(src);        /* skip username */
+                    socks4_hostname = name_based ? get_asciz(src) : NULL;
+
+                    if (get_err(src) == BSE_OUT_OF_DATA)
+                        return;
+                    if (get_err(src))
+                        goto socks4_reject;
+
+                    pf->port = port;
+                    if (name_based) {
+                        pf->hostname = dupstr(socks4_hostname);
+                    } else {
+                        pf->hostname = ipv4_to_string(ipv4);
+                    }
+
+                    output = strbuf_new();
+                    put_byte(output, 0);       /* reply version */
+                    put_byte(output, 90);      /* SOCKS 4 'request granted' */
+                    put_uint16(output, 0);     /* null port field */
+                    put_uint32(output, 0);     /* null address field */
+                    sk_write(pf->s, output->u, output->len);
+                    strbuf_free(output);
+
+                    pf->socks_state = SOCKS_NONE;
+                    pf->socksbuf_consumed = src->pos;
+                    break;
+                }
+
+              socks4_reject:
+                output = strbuf_new();
+                put_byte(output, 0);       /* reply version */
+                put_byte(output, 91);      /* SOCKS 4 'request rejected' */
+                put_uint16(output, 0);     /* null port field */
+                put_uint32(output, 0);     /* null address field */
+                sk_write(pf->s, output->u, output->len);
+                strbuf_free(output);
+                pfd_close(pf);
+                return;
+
+              case SOCKS_5_INITIAL:
+                /* SOCKS 5 initial method list */
+                socks_version = get_byte(src);
+                methods = get_pstring(src);
+
+                method = 0xFF;         /* means 'no usable method found' */
+                {
+                    int i;
+                    for (i = 0; i < methods.len; i++) {
+                        if (((const unsigned char *)methods.ptr)[i] == 0 ) {
+                            method = 0;        /* no auth */
+                            break;
+                        }
+                    }
+                }
+
+                if (get_err(src) == BSE_OUT_OF_DATA)
+                    return;
+                if (get_err(src))
+                    method = 0xFF;
+
+                output = strbuf_new();
+                put_byte(output, 5);       /* SOCKS version */
+                put_byte(output, method);  /* selected auth method */
+                sk_write(pf->s, output->u, output->len);
+                strbuf_free(output);
+
+                if (method == 0xFF) {
+                    pfd_close(pf);
+                    return;
+                }
+
+                pf->socks_state = SOCKS_5_CONNECT;
+                pf->socksbuf_consumed = src->pos;
+                break;
+
+              case SOCKS_5_CONNECT:
+                /* SOCKS 5 connect message */
+                socks_version = get_byte(src);
+                message_type = get_byte(src);
+                reserved_byte = get_byte(src);
+
+                if (socks_version == 5 && message_type == 1 &&
+                    reserved_byte == 0) {
+
+                    reply_code = 0;        /* success */
+
+                    switch (get_byte(src)) {
+                      case 1:              /* IPv4 */
+                        pf->hostname = ipv4_to_string(get_uint32(src));
+                        break;
+                      case 4:              /* IPv6 */
+                        pf->hostname = ipv6_to_string(get_data(src, 16));
+                        break;
+                      case 3:              /* unresolved domain name */
+                        pf->hostname = mkstr(get_pstring(src));
+                        break;
+                      default:
+                        pf->hostname = NULL;
+                        reply_code = 8;    /* address type not supported */
+                        break;
+                    }
+
+                    pf->port = get_uint16(src);
+                } else {
+                    reply_code = 7;        /* command not supported */
+                }
+
+                if (get_err(src) == BSE_OUT_OF_DATA)
+                    return;
+                if (get_err(src))
+                    reply_code = 1;        /* general server failure */
+
+                output = strbuf_new();
+                put_byte(output, 5);       /* SOCKS version */
+                put_byte(output, reply_code);
+                put_byte(output, 0);       /* reserved */
+                put_byte(output, 1);       /* IPv4 address follows */
+                put_uint32(output, 0);     /* bound IPv4 address (unused) */
+                put_uint16(output, 0);     /* bound port number (unused) */
+                sk_write(pf->s, output->u, output->len);
+                strbuf_free(output);
+
+                if (reply_code != 0) {
+                    pfd_close(pf);
+                    return;
+                }
+
+                pf->socks_state = SOCKS_NONE;
+                pf->socksbuf_consumed = src->pos;
+                break;
+            }
+        }
 
 	/*
 	 * We come here when we're ready to make an actual
 	 * connection.
 	 */
-	connect:
-        sfree(pf->socksbuf);
-        pf->socksbuf = NULL;
 
 	/*
 	 * Freeze the socket until the SSH server confirms the
@@ -385,17 +404,6 @@ static void pfd_receive(Plug plug, int urgent, char *data, int len)
 	    /* asks to forward to the specified host/port for this */
 	    wrap_send_port_open(pf->c, pf->hostname, pf->port, pf->s);
 	}
-	pf->dynamic = 0;
-
-	/*
-	 * If there's any data remaining in our current buffer,
-	 * save it to be sent on pfd_confirm().
-	 */
-	if (len > 0) {
-	    pf->buffer = snewn(len, char);
-	    memcpy(pf->buffer, data, len);
-	    pf->buflen = len;
-	}
     }
     if (pf->ready) {
 	if (sshfwd_write(pf->c, data, len) > 0) {
@@ -407,12 +415,20 @@ static void pfd_receive(Plug plug, int urgent, char *data, int len)
 
 static void pfd_sent(Plug plug, int bufsize)
 {
-    struct PortForwarding *pf = (struct PortForwarding *) plug;
+    struct PortForwarding *pf = FROMFIELD(plug, struct PortForwarding, plugvt);
 
     if (pf->c)
 	sshfwd_unthrottle(pf->c, bufsize);
 }
 
+static const Plug_vtable PortForwarding_plugvt = {
+    pfd_log,
+    pfd_closing,
+    pfd_receive,
+    pfd_sent,
+    NULL
+};
+
 /*
  * Called when receiving a PORT OPEN from the server to make a
  * connection to a destination host.
@@ -423,17 +439,9 @@ static void pfd_sent(Plug plug, int bufsize)
 char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port,
                   void *c, Conf *conf, int addressfamily)
 {
-    static const struct plug_function_table fn_table = {
-	pfd_log,
-	pfd_closing,
-	pfd_receive,
-	pfd_sent,
-	NULL
-    };
-
     SockAddr addr;
     const char *err;
-    char *dummy_realhost;
+    char *dummy_realhost = NULL;
     struct PortForwarding *pf;
 
     /*
@@ -452,15 +460,15 @@ char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port,
      * Open socket.
      */
     pf = *pf_ret = new_portfwd_state();
-    pf->fn = &fn_table;
+    pf->plugvt = &PortForwarding_plugvt;
     pf->throttled = pf->throttle_override = 0;
     pf->ready = 1;
     pf->c = c;
     pf->backhandle = NULL;	       /* we shouldn't need this */
-    pf->dynamic = 0;
+    pf->socks_state = SOCKS_NONE;
 
     pf->s = new_connection(addr, dummy_realhost, port,
-                           0, 1, 0, 0, (Plug) pf, conf);
+                           0, 1, 0, 0, &pf->plugvt, conf);
     sfree(dummy_realhost);
     if ((err = sk_socket_error(pf->s)) != NULL) {
         char *err_ret = dupstr(err);
@@ -479,26 +487,19 @@ char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port,
 
 static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
 {
-    static const struct plug_function_table fn_table = {
-	pfd_log,
-	pfd_closing,
-	pfd_receive,
-	pfd_sent,
-	NULL
-    };
     struct PortForwarding *pf;
     struct PortListener *pl;
     Socket s;
     const char *err;
 
-    pl = (struct PortListener *)p;
+    pl = FROMFIELD(p, struct PortListener, plugvt);
     pf = new_portfwd_state();
-    pf->fn = &fn_table;
+    pf->plugvt = &PortForwarding_plugvt;
 
     pf->c = NULL;
     pf->backhandle = pl->backhandle;
 
-    pf->s = s = constructor(ctx, (Plug) pf);
+    pf->s = s = constructor(ctx, &pf->plugvt);
     if ((err = sk_socket_error(s)) != NULL) {
 	free_portfwd_state(pf);
 	return err != NULL;
@@ -507,12 +508,14 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
     pf->throttled = pf->throttle_override = 0;
     pf->ready = 0;
 
-    if (pl->dynamic) {
-	pf->dynamic = 1;
+    if (pl->is_dynamic) {
+	pf->socks_state = SOCKS_INITIAL;
+        pf->socksbuf = strbuf_new();
+        pf->socksbuf_consumed = 0;
 	pf->port = 0;		       /* "hostname" buffer is so far empty */
 	sk_set_frozen(s, 0);	       /* we want to receive SOCKS _now_! */
     } else {
-	pf->dynamic = 0;
+	pf->socks_state = SOCKS_NONE;
 	pf->hostname = dupstr(pl->hostname);
 	pf->port = pl->port;	
 	pf->c = new_sock_channel(pl->backhandle, pf);
@@ -529,6 +532,13 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
     return 0;
 }
 
+static const Plug_vtable PortListener_plugvt = {
+    pfl_log,
+    pfl_closing,
+    NULL,                          /* recv */
+    NULL,                          /* send */
+    pfl_accepting
+};
 
 /*
  * Add a new port-forwarding listener from srcaddr:port -> desthost:destport.
@@ -540,14 +550,6 @@ char *pfl_listen(char *desthost, int destport, char *srcaddr,
                  int port, void *backhandle, Conf *conf,
                  struct PortListener **pl_ret, int address_family)
 {
-    static const struct plug_function_table fn_table = {
-	pfl_log,
-	pfl_closing,
-        NULL,                          /* recv */
-        NULL,                          /* send */
-	pfl_accepting
-    };
-
     const char *err;
     struct PortListener *pl;
 
@@ -555,16 +557,16 @@ char *pfl_listen(char *desthost, int destport, char *srcaddr,
      * Open socket.
      */
     pl = *pl_ret = new_portlistener_state();
-    pl->fn = &fn_table;
+    pl->plugvt = &PortListener_plugvt;
     if (desthost) {
 	pl->hostname = dupstr(desthost);
 	pl->port = destport;
-	pl->dynamic = 0;
+	pl->is_dynamic = FALSE;
     } else
-	pl->dynamic = 1;
+	pl->is_dynamic = TRUE;
     pl->backhandle = backhandle;
 
-    pl->s = new_listener(srcaddr, port, (Plug) pl,
+    pl->s = new_listener(srcaddr, port, &pl->plugvt,
                          !conf_get_int(conf, CONF_lport_acceptall),
                          conf, address_family);
     if ((err = sk_socket_error(pl->s)) != NULL) {
@@ -620,7 +622,7 @@ void pfd_override_throttle(struct PortForwarding *pf, int enable)
 /*
  * Called to send data down the raw connection.
  */
-int pfd_send(struct PortForwarding *pf, char *data, int len)
+int pfd_send(struct PortForwarding *pf, const void *data, int len)
 {
     if (pf == NULL)
 	return 0;
@@ -640,10 +642,11 @@ void pfd_confirm(struct PortForwarding *pf)
     pf->ready = 1;
     sk_set_frozen(pf->s, 0);
     sk_write(pf->s, NULL, 0);
-    if (pf->buffer) {
-	sshfwd_write(pf->c, pf->buffer, pf->buflen);
-	sfree(pf->buffer);
-	pf->buffer = NULL;
+    if (pf->socksbuf) {
+	sshfwd_write(pf->c, pf->socksbuf->u + pf->socksbuf_consumed,
+                     pf->socksbuf->len - pf->socksbuf_consumed);
+        strbuf_free(pf->socksbuf);
+        pf->socksbuf = NULL;
     }
 }
 

+ 151 - 155
source/putty/proxy.c

@@ -23,7 +23,7 @@
  * Call this when proxy negotiation is complete, so that this
  * socket can begin working normally.
  */
-void proxy_activate (Proxy_Socket p)
+void proxy_activate (ProxySocket *p)
 {
     void *data;
     int len;
@@ -73,14 +73,14 @@ void proxy_activate (Proxy_Socket p)
      * unfreezing the actual underlying socket.
      */
     if (!p->freeze)
-	sk_set_frozen((Socket)p, 0);
+	sk_set_frozen(&p->sockvt, 0);
 }
 
 /* basic proxy socket functions */
 
 static Plug sk_proxy_plug (Socket s, Plug p)
 {
-    Proxy_Socket ps = (Proxy_Socket) s;
+    ProxySocket *ps = FROMFIELD(s, ProxySocket, sockvt);
     Plug ret = ps->plug;
     if (p)
 	ps->plug = p;
@@ -89,16 +89,16 @@ static Plug sk_proxy_plug (Socket s, Plug p)
 
 static void sk_proxy_close (Socket s)
 {
-    Proxy_Socket ps = (Proxy_Socket) s;
+    ProxySocket *ps = FROMFIELD(s, ProxySocket, sockvt);
 
     sk_close(ps->sub_socket);
     sk_addr_free(ps->remote_addr);
     sfree(ps);
 }
 
-static int sk_proxy_write (Socket s, const char *data, int len)
+static int sk_proxy_write (Socket s, const void *data, int len)
 {
-    Proxy_Socket ps = (Proxy_Socket) s;
+    ProxySocket *ps = FROMFIELD(s, ProxySocket, sockvt);
 
     if (ps->state != PROXY_STATE_ACTIVE) {
 	bufchain_add(&ps->pending_output_data, data, len);
@@ -107,9 +107,9 @@ static int sk_proxy_write (Socket s, const char *data, int len)
     return sk_write(ps->sub_socket, data, len);
 }
 
-static int sk_proxy_write_oob (Socket s, const char *data, int len)
+static int sk_proxy_write_oob (Socket s, const void *data, int len)
 {
-    Proxy_Socket ps = (Proxy_Socket) s;
+    ProxySocket *ps = FROMFIELD(s, ProxySocket, sockvt);
 
     if (ps->state != PROXY_STATE_ACTIVE) {
 	bufchain_clear(&ps->pending_output_data);
@@ -122,7 +122,7 @@ static int sk_proxy_write_oob (Socket s, const char *data, int len)
 
 static void sk_proxy_write_eof (Socket s)
 {
-    Proxy_Socket ps = (Proxy_Socket) s;
+    ProxySocket *ps = FROMFIELD(s, ProxySocket, sockvt);
 
     if (ps->state != PROXY_STATE_ACTIVE) {
         ps->pending_eof = 1;
@@ -133,7 +133,7 @@ static void sk_proxy_write_eof (Socket s)
 
 static void sk_proxy_flush (Socket s)
 {
-    Proxy_Socket ps = (Proxy_Socket) s;
+    ProxySocket *ps = FROMFIELD(s, ProxySocket, sockvt);
 
     if (ps->state != PROXY_STATE_ACTIVE) {
 	ps->pending_flush = 1;
@@ -144,7 +144,7 @@ static void sk_proxy_flush (Socket s)
 
 static void sk_proxy_set_frozen (Socket s, int is_frozen)
 {
-    Proxy_Socket ps = (Proxy_Socket) s;
+    ProxySocket *ps = FROMFIELD(s, ProxySocket, sockvt);
 
     if (ps->state != PROXY_STATE_ACTIVE) {
 	ps->freeze = is_frozen;
@@ -183,7 +183,7 @@ static void sk_proxy_set_frozen (Socket s, int is_frozen)
 
 static const char * sk_proxy_socket_error (Socket s)
 {
-    Proxy_Socket ps = (Proxy_Socket) s;
+    ProxySocket *ps = FROMFIELD(s, ProxySocket, sockvt);
     if (ps->error != NULL || ps->sub_socket == NULL) {
 	return ps->error;
     }
@@ -195,8 +195,7 @@ static const char * sk_proxy_socket_error (Socket s)
 static void plug_proxy_log(Plug plug, int type, SockAddr addr, int port,
 			   const char *error_msg, int error_code)
 {
-    Proxy_Plug pp = (Proxy_Plug) plug;
-    Proxy_Socket ps = pp->proxy_socket;
+    ProxySocket *ps = FROMFIELD(plug, ProxySocket, plugvt);
 
     plug_log(ps->plug, type, addr, port, error_msg, error_code);
 }
@@ -204,8 +203,7 @@ static void plug_proxy_log(Plug plug, int type, SockAddr addr, int port,
 static void plug_proxy_closing (Plug p, const char *error_msg,
 				int error_code, int calling_back)
 {
-    Proxy_Plug pp = (Proxy_Plug) p;
-    Proxy_Socket ps = pp->proxy_socket;
+    ProxySocket *ps = FROMFIELD(p, ProxySocket, plugvt);
 
     if (ps->state != PROXY_STATE_ACTIVE) {
 	ps->closing_error_msg = error_msg;
@@ -219,8 +217,7 @@ static void plug_proxy_closing (Plug p, const char *error_msg,
 
 static void plug_proxy_receive (Plug p, int urgent, char *data, int len)
 {
-    Proxy_Plug pp = (Proxy_Plug) p;
-    Proxy_Socket ps = pp->proxy_socket;
+    ProxySocket *ps = FROMFIELD(p, ProxySocket, plugvt);
 
     if (ps->state != PROXY_STATE_ACTIVE) {
 	/* we will lose the urgentness of this data, but since most,
@@ -239,8 +236,7 @@ static void plug_proxy_receive (Plug p, int urgent, char *data, int len)
 
 static void plug_proxy_sent (Plug p, int bufsize)
 {
-    Proxy_Plug pp = (Proxy_Plug) p;
-    Proxy_Socket ps = pp->proxy_socket;
+    ProxySocket *ps = FROMFIELD(p, ProxySocket, plugvt);
 
     if (ps->state != PROXY_STATE_ACTIVE) {
 	ps->sent_bufsize = bufsize;
@@ -253,8 +249,7 @@ static void plug_proxy_sent (Plug p, int bufsize)
 static int plug_proxy_accepting(Plug p,
                                 accept_fn_t constructor, accept_ctx_t ctx)
 {
-    Proxy_Plug pp = (Proxy_Plug) p;
-    Proxy_Socket ps = pp->proxy_socket;
+    ProxySocket *ps = FROMFIELD(p, ProxySocket, plugvt);
 
     if (ps->state != PROXY_STATE_ACTIVE) {
 	ps->accepting_constructor = constructor;
@@ -401,36 +396,35 @@ SockAddr name_lookup(const char *host, int port, char **canonicalname,
     }
 }
 
+static const struct Socket_vtable ProxySocket_sockvt = {
+    sk_proxy_plug,
+    sk_proxy_close,
+    sk_proxy_write,
+    sk_proxy_write_oob,
+    sk_proxy_write_eof,
+    sk_proxy_flush,
+    sk_proxy_set_frozen,
+    sk_proxy_socket_error,
+    NULL, /* peer_info */
+};
+
+static const struct Plug_vtable ProxySocket_plugvt = {
+    plug_proxy_log,
+    plug_proxy_closing,
+    plug_proxy_receive,
+    plug_proxy_sent,
+    plug_proxy_accepting
+};
+
 Socket new_connection(SockAddr addr, const char *hostname,
 		      int port, int privport,
 		      int oobinline, int nodelay, int keepalive,
 		      Plug plug, Conf *conf)
 {
-    static const struct socket_function_table socket_fn_table = {
-	sk_proxy_plug,
-	sk_proxy_close,
-	sk_proxy_write,
-	sk_proxy_write_oob,
-	sk_proxy_write_eof,
-	sk_proxy_flush,
-	sk_proxy_set_frozen,
-	sk_proxy_socket_error,
-        NULL, /* peer_info */
-    };
-
-    static const struct plug_function_table plug_fn_table = {
-	plug_proxy_log,
-	plug_proxy_closing,
-	plug_proxy_receive,
-	plug_proxy_sent,
-	plug_proxy_accepting
-    };
-
     if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE &&
 	proxy_for_destination(addr, hostname, port, conf))
     {
-	Proxy_Socket ret;
-	Proxy_Plug pplug;
+	ProxySocket *ret;
 	SockAddr proxy_addr;
 	char *proxy_canonical_name;
         const char *proxy_type;
@@ -443,8 +437,9 @@ Socket new_connection(SockAddr addr, const char *hostname,
 	    NULL)
 	    return sret;
 
-	ret = snew(struct Socket_proxy_tag);
-	ret->fn = &socket_fn_table;
+	ret = snew(ProxySocket);
+	ret->sockvt = &ProxySocket_sockvt;
+	ret->plugvt = &ProxySocket_plugvt;
 	ret->conf = conf_copy(conf);
 	ret->plug = plug;
 	ret->remote_addr = addr;       /* will need to be freed on close */
@@ -478,7 +473,7 @@ Socket new_connection(SockAddr addr, const char *hostname,
             proxy_type = "Telnet";
 	} else {
 	    ret->error = "Proxy error: Unknown proxy method";
-	    return (Socket) ret;
+	    return &ret->sockvt;
 	}
 
         {
@@ -491,12 +486,6 @@ Socket new_connection(SockAddr addr, const char *hostname,
             sfree(logmsg);
         }
 
-	/* create the proxy plug to map calls from the actual
-	 * socket into our proxy socket layer */
-	pplug = snew(struct Plug_proxy_tag);
-	pplug->fn = &plug_fn_table;
-	pplug->proxy_socket = ret;
-
         {
             char *logmsg = dns_log_msg(conf_get_str(conf, CONF_proxy_host),
                                        conf_get_int(conf, CONF_addressfamily),
@@ -511,9 +500,8 @@ Socket new_connection(SockAddr addr, const char *hostname,
 				   conf_get_int(conf, CONF_addressfamily));
 	if (sk_addr_error(proxy_addr) != NULL) {
 	    ret->error = "Proxy error: Unable to resolve proxy host name";
-            sfree(pplug);
             sk_addr_free(proxy_addr);
-	    return (Socket)ret;
+	    return &ret->sockvt;
 	}
 	sfree(proxy_canonical_name);
 
@@ -533,19 +521,19 @@ Socket new_connection(SockAddr addr, const char *hostname,
 	ret->sub_socket = sk_new(proxy_addr,
 				 conf_get_int(conf, CONF_proxy_port),
 				 privport, oobinline,
-				 nodelay, keepalive, (Plug) pplug,
+				 nodelay, keepalive, &ret->plugvt,
 				 #ifdef MPEXT
 				 conf_get_int(conf, CONF_connect_timeout), conf_get_int(conf, CONF_sndbuf)
 				 #endif
 				 );
 	if (sk_socket_error(ret->sub_socket) != NULL)
-	    return (Socket) ret;
+	    return &ret->sockvt;
 
 	/* start the proxy negotiation process... */
 	sk_set_frozen(ret->sub_socket, 0);
 	ret->negotiate(ret, PROXY_CHANGE_NEW);
 
-	return (Socket) ret;
+	return &ret->sockvt;
     }
 
     /* no proxy, so just return the direct socket */
@@ -604,7 +592,7 @@ static int get_line_end (char * data, int len)
     return -1;
 }
 
-int proxy_http_negotiate (Proxy_Socket p, int change)
+int proxy_http_negotiate (ProxySocket *p, int change)
 {
     if (p->state == PROXY_STATE_NEW) {
 	/* we are just beginning the proxy negotiate process,
@@ -789,7 +777,7 @@ int proxy_http_negotiate (Proxy_Socket p, int change)
  */
 
 /* SOCKS version 4 */
-int proxy_socks4_negotiate (Proxy_Socket p, int change)
+int proxy_socks4_negotiate (ProxySocket *p, int change)
 {
     if (p->state == PROXY_CHANGE_NEW) {
 
@@ -803,47 +791,38 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change)
 	 *  user ID (variable length, null terminated string)
 	 */
 
-	int length, type, namelen;
-	char *command, addr[4], hostname[512];
-	char *username;
-
-	type = sk_addrtype(p->remote_addr);
-	if (type == ADDRTYPE_IPV6) {
+        strbuf *command = strbuf_new();
+        char hostname[512];
+        int write_hostname = FALSE;
+
+        put_byte(command, 4);          /* SOCKS version 4 */
+        put_byte(command, 1);          /* CONNECT command */
+        put_uint16(command, p->remote_port);
+
+	switch (sk_addrtype(p->remote_addr)) {
+          case ADDRTYPE_IPV4:
+            {
+                char addr[4];
+                sk_addrcopy(p->remote_addr, addr);
+                put_data(command, addr, 4);
+                break;
+            }
+          case ADDRTYPE_NAME:
+            sk_getaddr(p->remote_addr, hostname, lenof(hostname));
+            put_uint32(command, 1);
+            write_hostname = TRUE;
+            break;
+          case ADDRTYPE_IPV6:
             p->error = "Proxy error: SOCKS version 4 does not support IPv6";
-	    return 1;
-	} else if (type == ADDRTYPE_IPV4) {
-	    namelen = 0;
-	    sk_addrcopy(p->remote_addr, addr);
-	} else {		       /* type == ADDRTYPE_NAME */
-	    assert(type == ADDRTYPE_NAME);
-	    sk_getaddr(p->remote_addr, hostname, lenof(hostname));
-	    namelen = strlen(hostname) + 1;   /* include the NUL */
-	    addr[0] = addr[1] = addr[2] = 0;
-	    addr[3] = 1;
+            strbuf_free(command);
+            return 1;
 	}
 
-	username = conf_get_str(p->conf, CONF_proxy_username);
-	length = strlen(username) + namelen + 9;
-	command = snewn(length, char);
-	strcpy(command + 8, username);
-
-	command[0] = 4; /* version 4 */
-	command[1] = 1; /* CONNECT command */
-
-	/* port */
-	command[2] = (char) (p->remote_port >> 8) & 0xff;
-	command[3] = (char) p->remote_port & 0xff;
-
-	/* address */
-	memcpy(command + 4, addr, 4);
-
-	/* hostname */
-	memcpy(command + 8 + strlen(username) + 1,
-	       hostname, namelen);
-
-	sk_write(p->sub_socket, command, length);
-	sfree(username);
-	sfree(command);
+        put_asciz(command, conf_get_str(p->conf, CONF_proxy_username));
+        if (write_hostname)
+            put_asciz(command, hostname);
+	sk_write(p->sub_socket, command->s, command->len);
+	strbuf_free(command);
 
 	p->state = 1;
 	return 0;
@@ -949,7 +928,7 @@ int proxy_socks4_negotiate (Proxy_Socket p, int change)
 }
 
 /* SOCKS version 5 */
-int proxy_socks5_negotiate (Proxy_Socket p, int change)
+int proxy_socks5_negotiate (ProxySocket *p, int change)
 {
     if (p->state == PROXY_CHANGE_NEW) {
 
@@ -964,26 +943,30 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
 	 *     0x03 = CHAP
 	 */
 
-	char command[5];
+	strbuf *command;
 	char *username, *password;
-	int len;
+        int method_count_offset, methods_start;
 
-	command[0] = 5; /* version 5 */
+        command = strbuf_new();
+	put_byte(command, 5);          /* SOCKS version 5 */
 	username = conf_get_str(p->conf, CONF_proxy_username);
 	password = conf_get_str(p->conf, CONF_proxy_password);
+
+        method_count_offset = command->len;
+        put_byte(command, 0);
+        methods_start = command->len;
+
+        put_byte(command, 0x00);       /* no authentication */
+
 	if (username[0] || password[0]) {
-	    command[2] = 0x00;	       /* no authentication */
-	    len = 3;
-	    proxy_socks5_offerencryptedauth (command, &len);
-	    command[len++] = 0x02;	       /* username/password */
-	    command[1] = len - 2;	/* Number of methods supported */
-	} else {
-	    command[1] = 1;	       /* one methods supported: */
-	    command[2] = 0x00;	       /* no authentication */
-	    len = 3;
+	    proxy_socks5_offerencryptedauth(BinarySink_UPCAST(command));
+            put_byte(command, 0x02);    /* username/password */
 	}
 
-	sk_write(p->sub_socket, command, len);
+        command->u[method_count_offset] = command->len - methods_start;
+
+	sk_write(p->sub_socket, command->s, command->len);
+        strbuf_free(command);
 
 	p->state = 1;
 	return 0;
@@ -1121,36 +1104,40 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
 	     *  dest. port (2 bytes) [network order]
 	     */
 
-	    char command[512];
-	    int len;
-	    int type;
-
-	    type = sk_addrtype(p->remote_addr);
-	    if (type == ADDRTYPE_IPV4) {
-		len = 10;	       /* 4 hdr + 4 addr + 2 trailer */
-		command[3] = 1; /* IPv4 */
-		sk_addrcopy(p->remote_addr, command+4);
-	    } else if (type == ADDRTYPE_IPV6) {
-		len = 22;	       /* 4 hdr + 16 addr + 2 trailer */
-		command[3] = 4; /* IPv6 */
-		sk_addrcopy(p->remote_addr, command+4);
-	    } else {
-		assert(type == ADDRTYPE_NAME);
-		command[3] = 3;
-		sk_getaddr(p->remote_addr, command+5, 256);
-		command[4] = strlen(command+5);
-		len = 7 + command[4];  /* 4 hdr, 1 len, N addr, 2 trailer */
+	    strbuf *command = strbuf_new();
+	    put_byte(command, 5);      /* SOCKS version 5 */
+	    put_byte(command, 1);      /* CONNECT command */
+	    put_byte(command, 0x00);   /* reserved byte */
+
+	    switch (sk_addrtype(p->remote_addr)) {
+              case ADDRTYPE_IPV4:
+		put_byte(command, 1);  /* IPv4 */
+		sk_addrcopy(p->remote_addr, strbuf_append(command, 4));
+                break;
+              case ADDRTYPE_IPV6:
+		put_byte(command, 4);  /* IPv6 */
+		sk_addrcopy(p->remote_addr, strbuf_append(command, 16));
+                break;
+              case ADDRTYPE_NAME:
+                {
+                    char hostname[512];
+                    put_byte(command, 3);  /* domain name */
+                    sk_getaddr(p->remote_addr, hostname, lenof(hostname));
+                    if (!put_pstring(command, hostname)) {
+                        p->error = "Proxy error: SOCKS 5 cannot "
+                            "support host names longer than 255 chars";
+                        strbuf_free(command);
+                        return 1;
+                    }
+                }
+                break;
 	    }
 
-	    command[0] = 5; /* version 5 */
-	    command[1] = 1; /* CONNECT command */
-	    command[2] = 0x00;
+            put_uint16(command, p->remote_port);
 
-	    /* port */
-	    command[len-2] = (char) (p->remote_port >> 8) & 0xff;
-	    command[len-1] = (char) p->remote_port & 0xff;
+	    sk_write(p->sub_socket, command->s, command->len);
 
-	    sk_write(p->sub_socket, command, len);
+            strbuf_free(command);
 
 	    p->state = 3;
 	    return 1;
@@ -1249,23 +1236,25 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
 	}
 
 	if (p->state == 5) {
-	    char *username = conf_get_str(p->conf, CONF_proxy_username);
-	    char *password = conf_get_str(p->conf, CONF_proxy_password);
+            const char *username = conf_get_str(p->conf, CONF_proxy_username);
+            const char *password = conf_get_str(p->conf, CONF_proxy_password);
 	    if (username[0] || password[0]) {
-		char userpwbuf[255 + 255 + 3];
-		int ulen, plen;
-		ulen = strlen(username);
-		if (ulen > 255) ulen = 255;
-		if (ulen < 1) ulen = 1;
-		plen = strlen(password);
-		if (plen > 255) plen = 255;
-		if (plen < 1) plen = 1;
-		userpwbuf[0] = 1;      /* version number of subnegotiation */
-		userpwbuf[1] = ulen;
-		memcpy(userpwbuf+2, username, ulen);
-		userpwbuf[ulen+2] = plen;
-		memcpy(userpwbuf+ulen+3, password, plen);
-		sk_write(p->sub_socket, userpwbuf, ulen + plen + 3);
+                strbuf *auth = strbuf_new();
+		put_byte(auth, 1); /* version number of subnegotiation */
+                if (!put_pstring(auth, username)) {
+                    p->error = "Proxy error: SOCKS 5 authentication cannot "
+                        "support usernames longer than 255 chars";
+                    strbuf_free(auth);
+                    return 1;
+                }
+                if (!put_pstring(auth, password)) {
+                    p->error = "Proxy error: SOCKS 5 authentication cannot "
+                        "support passwords longer than 255 chars";
+                    strbuf_free(auth);
+                    return 1;
+                }
+		sk_write(p->sub_socket, auth->s, auth->len);
+                strbuf_free(auth);
 		p->state = 7;
 	    } else 
 		plug_closing(p->plug, "Proxy error: Server chose "
@@ -1515,7 +1504,7 @@ char *format_telnet_command(SockAddr addr, int port, Conf *conf)
 #undef ENSURE
 }
 
-int proxy_telnet_negotiate (Proxy_Socket p, int change)
+int proxy_telnet_negotiate (ProxySocket *p, int change)
 {
     if (p->state == PROXY_CHANGE_NEW) {
 	char *formatted_cmd;
@@ -1610,3 +1599,10 @@ int proxy_telnet_negotiate (Proxy_Socket p, int change)
 		 PROXY_ERROR_UNEXPECTED, 0);
     return 1;
 }
+
+#ifdef MPEXT
+ProxySocket * get_proxy_plug_socket(Plug p)
+{
+    return FROMFIELD(p, ProxySocket, plugvt);
+}
+#endif

+ 17 - 23
source/putty/proxy.h

@@ -13,12 +13,9 @@
 #define PROXY_ERROR_GENERAL 8000
 #define PROXY_ERROR_UNEXPECTED 8001
 
-typedef struct Socket_proxy_tag * Proxy_Socket;
-
-struct Socket_proxy_tag {
-    const struct socket_function_table *fn;
-    /* the above variable absolutely *must* be the first in this structure */
+typedef struct ProxySocket ProxySocket;
 
+struct ProxySocket {
     const char *error;
 
     Socket sub_socket;
@@ -58,7 +55,7 @@ struct Socket_proxy_tag {
      * and further the proxy negotiation process.
      */
 
-    int (*negotiate) (Proxy_Socket /* this */, int /* change type */);
+    int (*negotiate) (ProxySocket * /* this */, int /* change type */);
 
     /* current arguments of plug handlers
      * (for use by proxy's negotiate function)
@@ -89,24 +86,17 @@ struct Socket_proxy_tag {
     int chap_num_attributes_processed;
     int chap_current_attribute;
     int chap_current_datalen;
-};
-
-typedef struct Plug_proxy_tag * Proxy_Plug;
-
-struct Plug_proxy_tag {
-    const struct plug_function_table *fn;
-    /* the above variable absolutely *must* be the first in this structure */
-
-    Proxy_Socket proxy_socket;
 
+    const Socket_vtable *sockvt;
+    const Plug_vtable *plugvt;
 };
 
-extern void proxy_activate (Proxy_Socket);
+extern void proxy_activate (ProxySocket *);
 
-extern int proxy_http_negotiate (Proxy_Socket, int);
-extern int proxy_telnet_negotiate (Proxy_Socket, int);
-extern int proxy_socks4_negotiate (Proxy_Socket, int);
-extern int proxy_socks5_negotiate (Proxy_Socket, int);
+extern int proxy_http_negotiate (ProxySocket *, int);
+extern int proxy_telnet_negotiate (ProxySocket *, int);
+extern int proxy_socks4_negotiate (ProxySocket *, int);
+extern int proxy_socks5_negotiate (ProxySocket *, int);
 
 /*
  * This may be reused by local-command proxies on individual
@@ -118,8 +108,12 @@ char *format_telnet_command(SockAddr addr, int port, Conf *conf);
  * These are implemented in cproxy.c or nocproxy.c, depending on
  * whether encrypted proxy authentication is available.
  */
-extern void proxy_socks5_offerencryptedauth(char *command, int *len);
-extern int proxy_socks5_handlechap (Proxy_Socket p);
-extern int proxy_socks5_selectchap(Proxy_Socket p);
+extern void proxy_socks5_offerencryptedauth(BinarySink *);
+extern int proxy_socks5_handlechap (ProxySocket *);
+extern int proxy_socks5_selectchap(ProxySocket *);
+
+#ifdef MPEXT
+ProxySocket * get_proxy_plug_socket(Plug p);
+#endif
 
 #endif

+ 169 - 68
source/putty/putty.h

@@ -16,27 +16,31 @@
 #endif
 #endif
 
-#ifndef DONE_TYPEDEFS
-#define DONE_TYPEDEFS
-typedef struct conf_tag Conf;
-typedef struct backend_tag Backend;
-typedef struct terminal_tag Terminal;
-#endif
-
+#include "defs.h"
 #include "puttyps.h"
 #include "network.h"
 #include "misc.h"
+#include "marshal.h"
+
+/*
+ * We express various time intervals in unsigned long minutes, but may need to
+ * clip some values so that the resulting number of ticks does not overflow an
+ * integer value.
+ */
+#define MAX_TICK_MINS	(INT_MAX / (60 * TICKSPERSEC))
 
 /*
- * Fingerprints of the PGP master keys that can be used to establish a trust
- * path between an executable and other files.
+ * Fingerprints of the current and previous PGP master keys, to
+ * establish a trust path between an executable and other files.
  */
-#define PGP_MASTER_KEY_FP \
+#define PGP_MASTER_KEY_YEAR "2018"
+#define PGP_MASTER_KEY_DETAILS "RSA, 4096-bit"
+#define PGP_MASTER_KEY_FP                                       \
+    "24E1 B1C5 75EA 3C9F F752  A922 76BC 7FE4 EBFD 2D9E"
+#define PGP_PREV_MASTER_KEY_YEAR "2015"
+#define PGP_PREV_MASTER_KEY_DETAILS "RSA, 4096-bit"
+#define PGP_PREV_MASTER_KEY_FP                                  \
     "440D E3B5 B7A1 CA85 B3CC  1718 AB58 5DC6 0467 6F7C"
-#define PGP_RSA_MASTER_KEY_FP \
-    "8F 15 97 DA 25 30 AB 0D  88 D1 92 54 11 CF 0C 4C"
-#define PGP_DSA_MASTER_KEY_FP \
-    "313C 3E76 4B74 C2C5 F2AE  83A8 4F5E 6DF5 6A93 B34E"
 
 /* Three attribute types:
  * The ATTRs (normal attributes) are stored with the characters in
@@ -104,15 +108,16 @@ typedef struct terminal_tag Terminal;
  */
 #define UCSWIDE	     0xDFFF
 
-#define ATTR_NARROW  0x800000U
-#define ATTR_WIDE    0x400000U
-#define ATTR_BOLD    0x040000U
-#define ATTR_UNDER   0x080000U
-#define ATTR_REVERSE 0x100000U
-#define ATTR_BLINK   0x200000U
-#define ATTR_FGMASK  0x0001FFU
-#define ATTR_BGMASK  0x03FE00U
-#define ATTR_COLOURS 0x03FFFFU
+#define ATTR_NARROW  0x0800000U
+#define ATTR_WIDE    0x0400000U
+#define ATTR_BOLD    0x0040000U
+#define ATTR_UNDER   0x0080000U
+#define ATTR_REVERSE 0x0100000U
+#define ATTR_BLINK   0x0200000U
+#define ATTR_FGMASK  0x00001FFU
+#define ATTR_BGMASK  0x003FE00U
+#define ATTR_COLOURS 0x003FFFFU
+#define ATTR_DIM     0x1000000U
 #define ATTR_FGSHIFT 0
 #define ATTR_BGSHIFT 9
 
@@ -530,8 +535,6 @@ GLOBAL int loaded_session;
  */
 GLOBAL char *cmdline_session_name;
 
-struct RSAKey;			       /* be a little careful of scope */
-
 /*
  * Mechanism for getting text strings such as usernames and passwords
  * from the front-end.
@@ -592,12 +595,65 @@ void prompt_ensure_result_size(prompt_t *pr, int len);
 /* Burn the evidence. (Assumes _all_ strings want free()ing.) */
 void free_prompts(prompts_t *p);
 
+/*
+ * Data type definitions for true-colour terminal display.
+ * 'optionalrgb' describes a single RGB colour, which overrides the
+ * other colour settings if 'enabled' is nonzero, and is ignored
+ * otherwise. 'truecolour' contains a pair of those for foreground and
+ * background.
+ */
+typedef struct optionalrgb {
+    unsigned char enabled;
+    unsigned char r, g, b;
+} optionalrgb;
+extern const optionalrgb optionalrgb_none;
+typedef struct truecolour {
+    optionalrgb fg, bg;
+} truecolour;
+#define optionalrgb_equal(r1,r2) (                              \
+        (r1).enabled==(r2).enabled &&                           \
+        (r1).r==(r2).r && (r1).g==(r2).g && (r1).b==(r2).b)
+#define truecolour_equal(c1,c2) (               \
+        optionalrgb_equal((c1).fg, (c2).fg) &&  \
+        optionalrgb_equal((c1).bg, (c2).bg))
+
+/*
+ * Enumeration of clipboards. We provide some standard ones cross-
+ * platform, and then permit each platform to extend this enumeration
+ * further by defining PLATFORM_CLIPBOARDS in its own header file.
+ *
+ * CLIP_NULL is a non-clipboard, writes to which are ignored and reads
+ * from which return no data.
+ *
+ * CLIP_LOCAL refers to a buffer within terminal.c, which
+ * unconditionally saves the last data selected in the terminal. In
+ * configurations where a system clipboard is not written
+ * automatically on selection but instead by an explicit UI action,
+ * this is where the code responding to that action can find the data
+ * to write to the clipboard in question.
+ */
+#define CROSS_PLATFORM_CLIPBOARDS(X)                    \
+    X(CLIP_NULL, "null clipboard")                      \
+    X(CLIP_LOCAL, "last text selected in terminal")     \
+    /* end of list */
+
+#define ALL_CLIPBOARDS(X)                       \
+    CROSS_PLATFORM_CLIPBOARDS(X)                \
+    PLATFORM_CLIPBOARDS(X)                      \
+    /* end of list */
+
+#define CLIP_ID(id,name) id,
+enum { ALL_CLIPBOARDS(CLIP_ID) N_CLIPBOARDS };
+#undef CLIP_ID
+
 /*
  * Exports from the front end.
  */
 void request_resize(void *frontend, int, int);
-void do_text(Context, int, int, wchar_t *, int, unsigned long, int);
-void do_cursor(Context, int, int, wchar_t *, int, unsigned long, int);
+void do_text(Context, int, int, wchar_t *, int, unsigned long, int,
+             truecolour);
+void do_cursor(Context, int, int, wchar_t *, int, unsigned long, int,
+               truecolour);
 int char_width(Context ctx, int uc);
 #ifdef OPTIMISE_SCROLL
 void do_scroll(Context, int, int, int);
@@ -609,23 +665,21 @@ Context get_ctx(void *frontend);
 void free_ctx(Context);
 void palette_set(void *frontend, int, int, int, int);
 void palette_reset(void *frontend);
-void write_aclip(void *frontend, char *, int, int);
-void write_clip(void *frontend, wchar_t *, int *, int, int);
-void get_clip(void *frontend, wchar_t **, int *);
+int palette_get(void *frontend, int n, int *r, int *g, int *b);
+void write_clip(void *frontend, int clipboard, wchar_t *, int *,
+                truecolour *, int, int);
 void optimised_move(void *frontend, int, int, int);
 void set_raw_mouse_mode(void *frontend, int);
 void connection_fatal(void *frontend, const char *, ...);
 void nonfatal(const char *, ...);
-void fatalbox(const char *, ...);
 void modalfatalbox(const char *, ...);
 #ifdef macintosh
-#pragma noreturn(fatalbox)
 #pragma noreturn(modalfatalbox)
 #endif
 void do_beep(void *frontend, int);
 void begin_session(void *frontend);
 void sys_cursor(void *frontend, int x, int y);
-void request_paste(void *frontend);
+void frontend_request_paste(void *frontend, int clipboard);
 void frontend_keypress(void *frontend);
 void frontend_echoedit_update(void *frontend, int echo, int edit);
 /* It's the backend's responsibility to invoke this at the start of a
@@ -633,8 +687,8 @@ void frontend_echoedit_update(void *frontend, int echo, int edit);
  * special commands changes. It does not need to invoke it at session
  * shutdown. */
 void update_specials_menu(void *frontend);
-int from_backend(void *frontend, int is_stderr, const char *data, int len);
-int from_backend_untrusted(void *frontend, const char *data, int len);
+int from_backend(void *frontend, int is_stderr, const void *data, int len);
+int from_backend_untrusted(void *frontend, const void *data, int len);
 /* Called when the back end wants to indicate that EOF has arrived on
  * the server-to-client stream. Returns FALSE to indicate that we
  * intend to keep the session open in the other direction, or TRUE to
@@ -647,9 +701,9 @@ char *get_ttymode(void *frontend, const char *mode);
 /*
  * >0 = `got all results, carry on'
  * 0  = `user cancelled' (FIXME distinguish "give up entirely" and "next auth"?)
- * <0 = `please call back later with more in/inlen'
+ * <0 = `please call back later with a fuller bufchain'
  */
-int get_userpass_input(prompts_t *p, const unsigned char *in, int inlen);
+int get_userpass_input(prompts_t *p, bufchain *input);
 #define OPTIMISE_IS_SCROLL 1
 
 void set_iconic(void *frontend, int iconic);
@@ -735,8 +789,10 @@ void cleanup_exit(int);
     X(INT, NONE, ssh_show_banner) /* show USERAUTH_BANNERs (SSH-2 only) */ \
     X(INT, NONE, try_tis_auth) \
     X(INT, NONE, try_ki_auth) \
-    X(INT, NONE, try_gssapi_auth) /* attempt gssapi auth */ \
+    X(INT, NONE, try_gssapi_auth) /* attempt gssapi auth via ssh userauth */ \
+    X(INT, NONE, try_gssapi_kex) /* attempt gssapi auth via ssh kex */ \
     X(INT, NONE, gssapifwd) /* forward tgt via gss */ \
+    X(INT, NONE, gssapirekey) /* KEXGSS refresh interval (mins) */ \
     X(INT, INT, ssh_gsslist) /* preference order for local GSS libs */ \
     X(FILENAME, NONE, ssh_gss_custom) \
     X(INT, NONE, ssh_subsys) /* run a subsystem rather than a command */ \
@@ -835,6 +891,7 @@ void cleanup_exit(int);
     /* Colour options */ \
     X(INT, NONE, ansi_colour) \
     X(INT, NONE, xterm_256_colour) \
+    X(INT, NONE, true_colour) \
     X(INT, NONE, system_colour) \
     X(INT, NONE, try_palette) \
     X(INT, NONE, bold_style) \
@@ -842,10 +899,19 @@ void cleanup_exit(int);
     /* Selection options */ \
     X(INT, NONE, mouse_is_xterm) \
     X(INT, NONE, rect_select) \
+    X(INT, NONE, paste_controls) \
     X(INT, NONE, rawcnp) \
+    X(INT, NONE, utf8linedraw) \
     X(INT, NONE, rtf_paste) \
     X(INT, NONE, mouse_override) \
     X(INT, INT, wordness) \
+    X(INT, NONE, mouseautocopy) \
+    X(INT, NONE, mousepaste) \
+    X(INT, NONE, ctrlshiftins) \
+    X(INT, NONE, ctrlshiftcv) \
+    X(STR, NONE, mousepaste_custom) \
+    X(STR, NONE, ctrlshiftins_custom) \
+    X(STR, NONE, ctrlshiftcv_custom) \
     /* translations */ \
     X(INT, NONE, vtmode) \
     X(STR, NONE, line_codepage) \
@@ -956,9 +1022,8 @@ void conf_del_str_str(Conf *conf, int key, const char *subkey);
 void conf_set_filename(Conf *conf, int key, const Filename *val);
 void conf_set_fontspec(Conf *conf, int key, const FontSpec *val);
 /* Serialisation functions for Duplicate Session */
-int conf_serialised_size(Conf *conf);
-void conf_serialise(Conf *conf, void *data);
-int conf_deserialise(Conf *conf, void *data, int maxsize);/*returns size used*/
+void conf_serialise(BinarySink *bs, Conf *conf);
+int conf_deserialise(Conf *conf, BinarySource *src);/*returns true on success*/
 
 /*
  * Functions to copy, free, serialise and deserialise FontSpecs.
@@ -971,8 +1036,8 @@ int conf_deserialise(Conf *conf, void *data, int maxsize);/*returns size used*/
  */
 FontSpec *fontspec_copy(const FontSpec *f);
 void fontspec_free(FontSpec *f);
-int fontspec_serialise(FontSpec *f, void *data);
-FontSpec *fontspec_deserialise(void *data, int maxsize, int *used);
+void fontspec_serialise(BinarySink *bs, FontSpec *f);
+FontSpec *fontspec_deserialise(BinarySource *src);
 
 /*
  * Exports from noise.c.
@@ -1034,26 +1099,27 @@ void term_mouse(Terminal *, Mouse_Button, Mouse_Button, Mouse_Action,
 		int,int,int,int,int);
 void term_key(Terminal *, Key_Sym, wchar_t *, size_t, unsigned int,
 	      unsigned int);
-void term_deselect(Terminal *);
+void term_lost_clipboard_ownership(Terminal *, int clipboard);
 void term_update(Terminal *);
 void term_invalidate(Terminal *);
 void term_blink(Terminal *, int set_cursor);
-void term_do_paste(Terminal *);
+void term_do_paste(Terminal *, const wchar_t *, int);
 void term_nopaste(Terminal *);
 int term_ldisc(Terminal *, int option);
-void term_copyall(Terminal *);
+void term_copyall(Terminal *, const int *, int);
 void term_reconfig(Terminal *, Conf *);
-void term_seen_key_event(Terminal *);
-int term_data(Terminal *, int is_stderr, const char *data, int len);
-int term_data_untrusted(Terminal *, const char *data, int len);
+void term_request_copy(Terminal *, const int *clipboards, int n_clipboards);
+void term_request_paste(Terminal *, int clipboard);
+void term_seen_key_event(Terminal *); 
+int term_data(Terminal *, int is_stderr, const void *data, int len);
+int term_data_untrusted(Terminal *, const void *data, int len);
 void term_provide_resize_fn(Terminal *term,
 			    void (*resize_fn)(void *, int, int),
 			    void *resize_ctx);
 void term_provide_logctx(Terminal *term, void *logctx);
 void term_set_focus(Terminal *term, int has_focus);
 char *term_get_ttymode(Terminal *term, const char *mode);
-int term_get_userpass_input(Terminal *term, prompts_t *p,
-			    const unsigned char *in, int inlen);
+int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input);
 
 int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl);
 
@@ -1117,7 +1183,7 @@ extern Backend ssh_backend;
 void *ldisc_create(Conf *, Terminal *, Backend *, void *, void *);
 void ldisc_configure(void *, Conf *);
 void ldisc_free(void *);
-void ldisc_send(void *handle, const char *buf, int len, int interactive);
+void ldisc_send(void *handle, const void *buf, int len, int interactive);
 void ldisc_echoedit_update(void *handle);
 
 /*
@@ -1157,6 +1223,11 @@ void pinger_free(Pinger);
 int conf_launchable(Conf *conf);
 char const *conf_dest(Conf *conf);
 
+/*
+ * Exports from sessprep.c.
+ */
+void prepare_session(Conf *conf);
+
 /*
  * Exports from sercfg.c.
  */
@@ -1196,14 +1267,6 @@ int mk_wcswidth(const unsigned int *pwcs, size_t n);
 int mk_wcwidth_cjk(unsigned int ucs);
 int mk_wcswidth_cjk(const unsigned int *pwcs, size_t n);
 
-/*
- * Exports from mscrypto.c
- */
-#ifdef MSCRYPTOAPI
-int crypto_startup();
-void crypto_wrapup();
-#endif
-
 /*
  * Exports from pageantc.c.
  *
@@ -1229,10 +1292,10 @@ void crypto_wrapup();
  */
 typedef struct agent_pending_query agent_pending_query;
 agent_pending_query *agent_query(
-    void *in, int inlen, void **out, int *outlen,
+    strbuf *in, void **out, int *outlen,
     void (*callback)(void *, void *, int), void *callback_ctx);
 void agent_cancel_query(agent_pending_query *);
-void agent_query_synchronous(void *in, int inlen, void **out, int *outlen);
+void agent_query_synchronous(strbuf *in, void **out, int *outlen);
 int agent_exists(void);
 
 /*
@@ -1302,8 +1365,7 @@ void display_banner(void *frontend, const char* banner, int size);
  * that aren't equivalents to things in windlg.c et al.
  */
 extern int console_batch_mode;
-int console_get_userpass_input(prompts_t *p, const unsigned char *in,
-                               int inlen);
+int console_get_userpass_input(prompts_t *p);
 void console_provide_logctx(void *logctx);
 int is_interactive(void);
 
@@ -1332,9 +1394,15 @@ void printer_finish_job(printer_job *);
 int cmdline_process_param(const char *, char *, int, Conf *);
 void cmdline_run_saved(Conf *);
 void cmdline_cleanup(void);
-int cmdline_get_passwd_input(prompts_t *p, const unsigned char *in, int inlen);
+int cmdline_get_passwd_input(prompts_t *p);
+int cmdline_host_ok(Conf *);
 #define TOOLTYPE_FILETRANSFER 1
 #define TOOLTYPE_NONNETWORK 2
+#define TOOLTYPE_HOST_ARG 4
+#define TOOLTYPE_HOST_ARG_CAN_BE_SESSION 8
+#define TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX 16
+#define TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD 32
+#define TOOLTYPE_PORT_ARG 64
 extern int cmdline_tooltype;
 
 void cmdline_error(const char *, ...);
@@ -1380,6 +1448,16 @@ enum {
 };
 extern const char *const x11_authnames[];  /* declared in x11fwd.c */
 
+/*
+ * An enum for the copy-paste UI action configuration.
+ */
+enum {
+    CLIPUI_NONE,     /* UI action has no copy/paste effect */
+    CLIPUI_IMPLICIT, /* use the default clipboard implicit in mouse actions  */
+    CLIPUI_EXPLICIT, /* use the default clipboard for explicit Copy/Paste */
+    CLIPUI_CUSTOM,   /* use a named clipboard (on systems that support it) */
+};
+
 /*
  * Miscellaneous exports from the platform-specific code.
  *
@@ -1392,11 +1470,12 @@ int filename_equal(const Filename *f1, const Filename *f2);
 int filename_is_null(const Filename *fn);
 Filename *filename_copy(const Filename *fn);
 void filename_free(Filename *fn);
-int filename_serialise(const Filename *f, void *data);
-Filename *filename_deserialise(void *data, int maxsize, int *used);
+void filename_serialise(BinarySink *bs, const Filename *f);
+Filename *filename_deserialise(BinarySource *src);
 char *get_username(void);	       /* return value needs freeing */
 char *get_random_data(int bytes, const char *device); /* used in cmdgen.c */
 char filename_char_sanitise(char c);   /* rewrite special pathname chars */
+int open_for_write_would_lose_data(const Filename *fn);
 
 /*
  * Exports and imports from timing.c.
@@ -1515,11 +1594,33 @@ unsigned long timing_last_clock(void);
  * actually running it (e.g. so as to put a zero timeout on a select()
  * call) then it can call toplevel_callback_pending(), which will
  * return true if at least one callback is in the queue.
+ *
+ * run_toplevel_callbacks() returns TRUE if it ran any actual code.
+ * This can be used as a means of speculatively terminating a select
+ * loop, as in PSFTP, for example - if a callback has run then perhaps
+ * it might have done whatever the loop's caller was waiting for.
  */
 typedef void (*toplevel_callback_fn_t)(void *ctx);
 void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx);
-void run_toplevel_callbacks(void);
+int run_toplevel_callbacks(void);
 int toplevel_callback_pending(void);
+void delete_callbacks_for_context(void *ctx);
+
+/*
+ * Another facility in callback.c deals with 'idempotent' callbacks,
+ * defined as those which never need to be scheduled again if they are
+ * already scheduled and have not yet run. (An example would be one
+ * which, when called, empties a queue of data completely: when data
+ * is added to the queue, you must ensure a run of the queue-consuming
+ * function has been scheduled, but if one is already pending, you
+ * don't need to schedule a second one.)
+ */
+struct IdempotentCallback {
+    toplevel_callback_fn_t fn;
+    void *ctx;
+    int queued;
+};
+void queue_idempotent_callback(struct IdempotentCallback *ic);
 
 typedef void (*toplevel_callback_notify_fn_t)(void *frontend);
 void request_callback_notifications(toplevel_callback_notify_fn_t notify,

+ 6 - 6
source/putty/puttyexp.h

@@ -1,28 +1,29 @@
 #ifndef PUTTY_PUTTYEXP_H
 #define PUTTY_PUTTYEXP_H
 
+#include "defs.h"
+
 // from ssh.c
 
 void ssh_close(void * handle);
 int is_ssh(void * handle);
 void call_ssh_timer(void * handle);
 int get_ssh_version(void * handle);
-void * get_ssh_frontend(void * handle);
+void * get_ssh_frontend(Plug plug);
 int get_ssh1_compressing(void * handle);
 const struct ssh_cipher * get_cipher(void * handle);
 const struct ssh2_cipher * get_cscipher(void * handle);
 const struct ssh2_cipher * get_sccipher(void * handle);
 const struct ssh_compress * get_cscomp(void * handle);
 const struct ssh_compress * get_sccomp(void * handle);
-int get_ssh_state(void * handle);
 int get_ssh_state_closed(void * handle);
 int get_ssh_state_session(void * handle);
 int get_ssh_exitcode(void * handle);
 const unsigned int * ssh2_remmaxpkt(void * handle);
 const unsigned int * ssh2_remwindow(void * handle);
 void md5checksum(const char * buffer, int len, unsigned char output[16]);
-typedef const struct ssh_signkey * cp_ssh_signkey;
-void get_hostkey_algs(int * count, cp_ssh_signkey * SignKeys);
+typedef const struct ssh_keyalg * cp_ssh_keyalg;
+void get_hostkey_algs(int * count, cp_ssh_keyalg * SignKeys);
 void get_macs(int * count, const struct ssh_mac *** amacs);
 
 // from wingss.c
@@ -66,11 +67,10 @@ extern const struct ssh_compress ssh_zlib;
 
 void * call_aes_make_context();
 void call_aes_free_context(void * handle);
-void call_aes_setup(void * ctx, int blocklen, unsigned char * key, int keylen);
+void call_aes_setup(void * ctx, unsigned char * key, int keylen);
 void call_aes_encrypt(void * ctx, unsigned int * block);
 void call_aes_decrypt(void * ctx, unsigned int * block);
 void call_aes_sdctr(unsigned char *blk, int len, void *ctx);
-void aes_iv(void *handle, unsigned char *iv);
 
 // from winmisc.c
 

+ 15 - 0
source/putty/puttymem.h

@@ -49,4 +49,19 @@ void safefree(void *);
     ((type *)snrealloc(sizeof((type *)0 == (ptr)) ? (ptr) : (ptr),      \
                        (n), sizeof(type)))
 
+/*
+ * For cases where you want to allocate a struct plus a subsidiary
+ * data buffer in one step, this macro lets you add a constant to the
+ * amount malloced.
+ *
+ * Since the return value is already cast to the struct type, a
+ * pointer to that many bytes of extra data can be conveniently
+ * obtained by simply adding 1 to the returned pointer!
+ * snew_plus_get_aux is a handy macro that does that and casts the
+ * result to void *, so you can assign it straight to wherever you
+ * wanted it.
+ */
+#define snew_plus(type, extra) ((type *)snmalloc(1, sizeof(type) + (extra)))
+#define snew_plus_get_aux(ptr) ((void *)((ptr) + 1))
+
 #endif

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 378 - 1794
source/putty/ssh.c


+ 338 - 169
source/putty/ssh.h

@@ -1,17 +1,19 @@
-#ifndef WINSCP_VS
 #include <stdio.h>
 #include <string.h>
 
 #include "puttymem.h"
 #include "tree234.h"
+#ifndef WINSCP_VS
 #include "network.h"
+#endif
 #include "int64.h"
 #include "misc.h"
 
+#ifndef WINSCP_VS
 struct ssh_channel;
 typedef struct ssh_tag *Ssh;
 
-extern int sshfwd_write(struct ssh_channel *c, char *, int);
+extern int sshfwd_write(struct ssh_channel *c, const void *, int);
 extern void sshfwd_write_eof(struct ssh_channel *c);
 extern void sshfwd_unclean_close(struct ssh_channel *c, const char *err);
 extern void sshfwd_unthrottle(struct ssh_channel *c, int bufsize);
@@ -23,11 +25,139 @@ void sshfwd_x11_sharing_handover(struct ssh_channel *c,
                                  const void *initial_data, int initial_len);
 void sshfwd_x11_is_local(struct ssh_channel *c);
 
-extern Socket ssh_connection_sharing_init(const char *host, int port,
-                                          Conf *conf, Ssh ssh, void **state);
+/*
+ * Buffer management constants. There are several of these for
+ * various different purposes:
+ *
+ *  - SSH1_BUFFER_LIMIT is the amount of backlog that must build up
+ *    on a local data stream before we throttle the whole SSH
+ *    connection (in SSH-1 only). Throttling the whole connection is
+ *    pretty drastic so we set this high in the hope it won't
+ *    happen very often.
+ *
+ *  - SSH_MAX_BACKLOG is the amount of backlog that must build up
+ *    on the SSH connection itself before we defensively throttle
+ *    _all_ local data streams. This is pretty drastic too (though
+ *    thankfully unlikely in SSH-2 since the window mechanism should
+ *    ensure that the server never has any need to throttle its end
+ *    of the connection), so we set this high as well.
+ *
+ *  - OUR_V2_WINSIZE is the default window size we present on SSH-2
+ *    channels.
+ *
+ *  - OUR_V2_BIGWIN is the window size we advertise for the only
+ *    channel in a simple connection.  It must be <= INT_MAX.
+ *
+ *  - OUR_V2_MAXPKT is the official "maximum packet size" we send
+ *    to the remote side. This actually has nothing to do with the
+ *    size of the _packet_, but is instead a limit on the amount
+ *    of data we're willing to receive in a single SSH2 channel
+ *    data message.
+ *
+ *  - OUR_V2_PACKETLIMIT is actually the maximum size of SSH
+ *    _packet_ we're prepared to cope with.  It must be a multiple
+ *    of the cipher block size, and must be at least 35000.
+ */
+
+#define SSH1_BUFFER_LIMIT 32768
+#define SSH_MAX_BACKLOG 32768
+#define OUR_V2_WINSIZE 16384
+#define OUR_V2_BIGWIN 0x7fffffff
+#define OUR_V2_MAXPKT 0x4000UL
+#define OUR_V2_PACKETLIMIT 0x9000UL
+
+typedef struct PacketQueueNode PacketQueueNode;
+struct PacketQueueNode {
+    PacketQueueNode *next, *prev;
+};
+
+typedef struct PktIn {
+    int refcount;
+    int type;
+    unsigned long sequence; /* SSH-2 incoming sequence number */
+    long encrypted_len;	    /* for SSH-2 total-size counting */
+    PacketQueueNode qnode;  /* for linking this packet on to a queue */
+    BinarySource_IMPLEMENTATION;
+} PktIn;
+
+#endif
+typedef struct PktOut {
+    long prefix;            /* bytes up to and including type field */
+    long length;            /* total bytes, including prefix */
+    int type;
+    long minlen;            /* SSH-2: ensure wire length is at least this */
+    unsigned char *data;    /* allocated storage */
+    long maxlen;	    /* amount of storage allocated for `data' */
+    long encrypted_len;	    /* for SSH-2 total-size counting */
+
+    /* Extra metadata used in SSH packet logging mode, allowing us to
+     * log in the packet header line that the packet came from a
+     * connection-sharing downstream and what if anything unusual was
+     * done to it. The additional_log_text field is expected to be a
+     * static string - it will not be freed. */
+    unsigned downstream_id;
+    const char *additional_log_text;
+
+    BinarySink_IMPLEMENTATION;
+} PktOut;
+#ifndef WINSCP_VS
+
+typedef struct PacketQueue {
+    PacketQueueNode end;
+} PacketQueue;
+
+void pq_init(struct PacketQueue *pq);
+void pq_push(struct PacketQueue *pq, PktIn *pkt);
+void pq_push_front(struct PacketQueue *pq, PktIn *pkt);
+PktIn *pq_peek(struct PacketQueue *pq);
+PktIn *pq_pop(struct PacketQueue *pq);
+void pq_clear(struct PacketQueue *pq);
+int pq_empty_on_to_front_of(struct PacketQueue *src, struct PacketQueue *dest);
+
+/*
+ * Packet type contexts, so that ssh2_pkt_type can correctly decode
+ * the ambiguous type numbers back into the correct type strings.
+ */
+typedef enum {
+    SSH2_PKTCTX_NOKEX,
+    SSH2_PKTCTX_DHGROUP,
+    SSH2_PKTCTX_DHGEX,
+    SSH2_PKTCTX_ECDHKEX,
+    SSH2_PKTCTX_GSSKEX,
+    SSH2_PKTCTX_RSAKEX
+} Pkt_KCtx;
+typedef enum {
+    SSH2_PKTCTX_NOAUTH,
+    SSH2_PKTCTX_PUBLICKEY,
+    SSH2_PKTCTX_PASSWORD,
+    SSH2_PKTCTX_GSSAPI,
+    SSH2_PKTCTX_KBDINTER
+} Pkt_ACtx;
+
+typedef struct PacketLogSettings {
+    int omit_passwords, omit_data;
+    Pkt_KCtx kctx;
+    Pkt_ACtx actx;
+} PacketLogSettings;
+
+#define MAX_BLANKS 4 /* no packet needs more censored sections than this */
+int ssh1_censor_packet(
+    const PacketLogSettings *pls, int type, int sender_is_client,
+    ptrlen pkt, logblank_t *blanks);
+int ssh2_censor_packet(
+    const PacketLogSettings *pls, int type, int sender_is_client,
+    ptrlen pkt, logblank_t *blanks);
+
+PktOut *ssh_new_packet(void);
+void ssh_unref_packet(PktIn *pkt);
+void ssh_free_pktout(PktOut *pkt);
+
+extern Socket ssh_connection_sharing_init(
+    const char *host, int port, Conf *conf, Ssh ssh, Plug sshplug,
+    void **state);
 int ssh_share_test_for_upstream(const char *host, int port, Conf *conf);
 void share_got_pkt_from_server(void *ctx, int type,
-                               unsigned char *pkt, int pktlen);
+                               const void *pkt, int pktlen);
 void share_activate(void *state, const char *server_verstring);
 void sharestate_free(void *state);
 int share_ndownstreams(void *state);
@@ -38,6 +168,8 @@ unsigned ssh_alloc_sharing_channel(Ssh ssh, void *sharing_ctx);
 void ssh_delete_sharing_channel(Ssh ssh, unsigned localid);
 int ssh_alloc_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
                                void *share_ctx);
+void ssh_remove_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
+                                 void *share_ctx);
 void ssh_sharing_queue_global_request(Ssh ssh, void *share_ctx);
 struct X11FakeAuth *ssh_sharing_add_x11_display(Ssh ssh, int authtype,
                                                 void *share_cs,
@@ -71,35 +203,29 @@ void share_setup_x11_channel(void *csv, void *chanv,
 #define SSH_CIPHER_3DES		3
 #define SSH_CIPHER_BLOWFISH	6
 
-#ifdef MSCRYPTOAPI
-#define APIEXTRA 8
-#else
-#define APIEXTRA 0
-#endif
-
 #ifndef BIGNUM_INTERNAL
 typedef void *Bignum;
 #endif
 
+typedef struct ssh_keyalg ssh_keyalg;
+typedef const struct ssh_keyalg *ssh_key;
+
 struct RSAKey {
     int bits;
     int bytes;
-#ifdef MSCRYPTOAPI
-    unsigned long exponent;
-    unsigned char *modulus;
-#else
     Bignum modulus;
     Bignum exponent;
     Bignum private_exponent;
     Bignum p;
     Bignum q;
     Bignum iqmp;
-#endif
     char *comment;
+    ssh_key sshk;
 };
 
 struct dss_key {
     Bignum p, q, g, y, x;
+    ssh_key sshk;
 };
 
 struct ec_curve;
@@ -153,48 +279,52 @@ struct ec_curve {
     };
 };
 
-const struct ssh_signkey *ec_alg_by_oid(int len, const void *oid,
+const ssh_keyalg *ec_alg_by_oid(int len, const void *oid,
                                         const struct ec_curve **curve);
-const unsigned char *ec_alg_oid(const struct ssh_signkey *alg, int *oidlen);
+const unsigned char *ec_alg_oid(const ssh_keyalg *alg, int *oidlen);
 extern const int ec_nist_curve_lengths[], n_ec_nist_curve_lengths;
 int ec_nist_alg_and_curve_by_bits(int bits,
                                   const struct ec_curve **curve,
-                                  const struct ssh_signkey **alg);
+                                  const ssh_keyalg **alg);
 int ec_ed_alg_and_curve_by_bits(int bits,
                                 const struct ec_curve **curve,
-                                const struct ssh_signkey **alg);
-
-struct ssh_signkey;
+                                const ssh_keyalg **alg);
 
 struct ec_key {
-    const struct ssh_signkey *signalg;
     struct ec_point publicKey;
     Bignum privateKey;
+    ssh_key sshk;
 };
 
 struct ec_point *ec_public(const Bignum privateKey, const struct ec_curve *curve);
 
-int makekey(const unsigned char *data, int len, struct RSAKey *result,
-	    const unsigned char **keystr, int order);
-int makeprivate(const unsigned char *data, int len, struct RSAKey *result);
-int rsaencrypt(unsigned char *data, int length, struct RSAKey *key);
-Bignum rsadecrypt(Bignum input, struct RSAKey *key);
-void rsasign(unsigned char *data, int length, struct RSAKey *key);
+/*
+ * SSH-1 never quite decided which order to store the two components
+ * of an RSA key. During connection setup, the server sends its host
+ * and server keys with the exponent first; private key files store
+ * the modulus first. The agent protocol is even more confusing,
+ * because the client specifies a key to the server in one order and
+ * the server lists the keys it knows about in the other order!
+ */
+typedef enum { RSA_SSH1_EXPONENT_FIRST, RSA_SSH1_MODULUS_FIRST } RsaSsh1Order;
+
+void BinarySource_get_rsa_ssh1_pub(
+    BinarySource *src, struct RSAKey *result, RsaSsh1Order order);
+void BinarySource_get_rsa_ssh1_priv(
+    BinarySource *src, struct RSAKey *rsa);
+int rsa_ssh1_encrypt(unsigned char *data, int length, struct RSAKey *key);
+Bignum rsa_ssh1_decrypt(Bignum input, struct RSAKey *key);
 void rsasanitise(struct RSAKey *key);
 int rsastr_len(struct RSAKey *key);
 void rsastr_fmt(char *str, struct RSAKey *key);
-void rsa_fingerprint(char *str, int len, struct RSAKey *key);
+char *rsa_ssh1_fingerprint(struct RSAKey *key);
 int rsa_verify(struct RSAKey *key);
-unsigned char *rsa_public_blob(struct RSAKey *key, int *len);
-int rsa_public_blob_len(void *data, int maxlen);
+void rsa_ssh1_public_blob(BinarySink *bs, struct RSAKey *key,
+                          RsaSsh1Order order);
+int rsa_ssh1_public_blob_len(void *data, int maxlen);
 void freersakey(struct RSAKey *key);
 #endif // WINSCP_VS
 
-#ifndef PUTTY_UINT32_DEFINED
-/* This makes assumptions about the int type. */
-typedef unsigned int uint32;
-#define PUTTY_UINT32_DEFINED
-#endif
 typedef uint32 word32;
 
 #ifndef WINSCP_VS
@@ -211,25 +341,22 @@ int detect_attack(void *handle, unsigned char *buf, uint32 len,
  * SSH2 RSA key exchange functions
  */
 struct ssh_hash;
-struct ssh_rsa_kex_extra {
-    int minklen;
-};
-void *ssh_rsakex_newkey(char *data, int len);
-void ssh_rsakex_freekey(void *key);
-int ssh_rsakex_klen(void *key);
+struct RSAKey *ssh_rsakex_newkey(const void *data, int len);
+void ssh_rsakex_freekey(struct RSAKey *key);
+int ssh_rsakex_klen(struct RSAKey *key);
 void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
-                        unsigned char *out, int outlen,
-                        void *key);
+                        unsigned char *out, int outlen, struct RSAKey *key);
 
 /*
  * SSH2 ECDH key exchange functions
  */
 struct ssh_kex;
 const char *ssh_ecdhkex_curve_textname(const struct ssh_kex *kex);
-void *ssh_ecdhkex_newkey(const struct ssh_kex *kex);
-void ssh_ecdhkex_freekey(void *key);
-char *ssh_ecdhkex_getpublic(void *key, int *len);
-Bignum ssh_ecdhkex_getkey(void *key, char *remoteKey, int remoteKeyLen);
+struct ec_key *ssh_ecdhkex_newkey(const struct ssh_kex *kex);
+void ssh_ecdhkex_freekey(struct ec_key *key);
+void ssh_ecdhkex_getpublic(struct ec_key *key, BinarySink *bs);
+Bignum ssh_ecdhkex_getkey(struct ec_key *key,
+                          const void *remoteKey, int remoteKeyLen);
 
 /*
  * Helper function for k generation in DSA, reused in ECDSA
@@ -242,19 +369,14 @@ typedef struct {
 } MD5_Core_State;
 
 struct MD5Context {
-#ifdef MSCRYPTOAPI
-    unsigned long hHash;
-#else
     MD5_Core_State core;
     unsigned char block[64];
     int blkused;
     uint32 lenhi, lenlo;
-#endif
+    BinarySink_IMPLEMENTATION;
 };
 
 void MD5Init(struct MD5Context *context);
-void MD5Update(struct MD5Context *context, unsigned char const *buf,
-	       unsigned len);
 void MD5Final(unsigned char digest[16], struct MD5Context *context);
 void MD5Simple(void const *p, unsigned len, unsigned char output[16]);
 
@@ -264,6 +386,8 @@ void hmacmd5_key(void *handle, void const *key, int len);
 void hmacmd5_do_hmac(void *handle, unsigned char const *blk, int len,
 		     unsigned char *hmac);
 
+int supports_sha_ni(void);
+
 #ifdef MPEXT
 // Resolve ambiguity with OpenSSL
 #define SHA_Init putty_SHA_Init
@@ -274,29 +398,31 @@ void hmacmd5_do_hmac(void *handle, unsigned char const *blk, int len,
 #define SHA512_Final putty_SHA512_Final
 #endif
 
-typedef struct {
+typedef struct SHA_State {
     uint32 h[5];
     unsigned char block[64];
     int blkused;
     uint32 lenhi, lenlo;
+    void (*sha1)(struct SHA_State * s, const unsigned char *p, int len);
+    BinarySink_IMPLEMENTATION;
 } SHA_State;
 void SHA_Init(SHA_State * s);
-void SHA_Bytes(SHA_State * s, const void *p, int len);
 void SHA_Final(SHA_State * s, unsigned char *output);
 void SHA_Simple(const void *p, int len, unsigned char *output);
 
 void hmac_sha1_simple(void *key, int keylen, void *data, int datalen,
 		      unsigned char *output);
 #endif // WINSCP_VS
-typedef struct {
+typedef struct SHA256_State {
     uint32 h[8];
     unsigned char block[64];
     int blkused;
     uint32 lenhi, lenlo;
+    void (*sha256)(struct SHA256_State * s, const unsigned char *p, int len);
+    BinarySink_IMPLEMENTATION;
 } SHA256_State;
 #ifndef WINSCP_VS
 void SHA256_Init(SHA256_State * s);
-void SHA256_Bytes(SHA256_State * s, const void *p, int len);
 void SHA256_Final(SHA256_State * s, unsigned char *output);
 void SHA256_Simple(const void *p, int len, unsigned char *output);
 
@@ -305,14 +431,13 @@ typedef struct {
     unsigned char block[128];
     int blkused;
     uint32 len[4];
+    BinarySink_IMPLEMENTATION;
 } SHA512_State;
 #define SHA384_State SHA512_State
 void SHA512_Init(SHA512_State * s);
-void SHA512_Bytes(SHA512_State * s, const void *p, int len);
 void SHA512_Final(SHA512_State * s, unsigned char *output);
 void SHA512_Simple(const void *p, int len, unsigned char *output);
 void SHA384_Init(SHA384_State * s);
-#define SHA384_Bytes(s, p, len) SHA512_Bytes(s, p, len)
 void SHA384_Final(SHA384_State * s, unsigned char *output);
 void SHA384_Simple(const void *p, int len, unsigned char *output);
 
@@ -320,9 +445,9 @@ struct ssh_mac;
 struct ssh_cipher {
     void *(*make_context)(void);
     void (*free_context)(void *);
-    void (*sesskey) (void *, unsigned char *key);	/* for SSH-1 */
-    void (*encrypt) (void *, unsigned char *blk, int len);
-    void (*decrypt) (void *, unsigned char *blk, int len);
+    void (*sesskey) (void *, const void *key);	/* for SSH-1 */
+    void (*encrypt) (void *, void *blk, int len);
+    void (*decrypt) (void *, void *blk, int len);
     int blksize;
     const char *text_name;
 };
@@ -330,13 +455,13 @@ struct ssh_cipher {
 struct ssh2_cipher {
     void *(*make_context)(void);
     void (*free_context)(void *);
-    void (*setiv) (void *, unsigned char *key);	/* for SSH-2 */
-    void (*setkey) (void *, unsigned char *key);/* for SSH-2 */
-    void (*encrypt) (void *, unsigned char *blk, int len);
-    void (*decrypt) (void *, unsigned char *blk, int len);
+    void (*setiv) (void *, const void *iv);	/* for SSH-2 */
+    void (*setkey) (void *, const void *key);/* for SSH-2 */
+    void (*encrypt) (void *, void *blk, int len);
+    void (*decrypt) (void *, void *blk, int len);
     /* Ignored unless SSH_CIPHER_SEPARATE_LENGTH flag set */
-    void (*encrypt_length) (void *, unsigned char *blk, int len, unsigned long seq);
-    void (*decrypt_length) (void *, unsigned char *blk, int len, unsigned long seq);
+    void (*encrypt_length) (void *, void *blk, int len, unsigned long seq);
+    void (*decrypt_length) (void *, void *blk, int len, unsigned long seq);
     const char *name;
     int blksize;
     /* real_keybits is the number of bits of entropy genuinely used by
@@ -369,13 +494,13 @@ struct ssh_mac {
     /* Passes in the cipher context */
     void *(*make_context)(void *);
     void (*free_context)(void *);
-    void (*setkey) (void *, unsigned char *key);
+    void (*setkey) (void *, const void *key);
     /* whole-packet operations */
-    void (*generate) (void *, unsigned char *blk, int len, unsigned long seq);
-    int (*verify) (void *, unsigned char *blk, int len, unsigned long seq);
+    void (*generate) (void *, void *blk, int len, unsigned long seq);
+    int (*verify) (void *, const void *blk, int len, unsigned long seq);
     /* partial-packet operations */
     void (*start) (void *);
-    void (*bytes) (void *, unsigned char const *, int);
+    BinarySink *(*sink) (void *);
     void (*genresult) (void *, unsigned char *);
     int (*verresult) (void *, unsigned char const *);
     const char *name, *etm_name;
@@ -386,7 +511,7 @@ struct ssh_mac {
 struct ssh_hash {
     void *(*init)(void); /* also allocates context */
     void *(*copy)(const void *);
-    void (*bytes)(void *, const void *, int);
+    BinarySink *(*sink) (void *);
     void (*final)(void *, unsigned char *); /* also frees context */
     void (*free)(void *);
     int hlen; /* output length in bytes */
@@ -395,7 +520,7 @@ struct ssh_hash {
 
 struct ssh_kex {
     const char *name, *groupname;
-    enum { KEXTYPE_DH, KEXTYPE_RSA, KEXTYPE_ECDH } main_type;
+    enum { KEXTYPE_DH, KEXTYPE_RSA, KEXTYPE_ECDH, KEXTYPE_GSS } main_type;
     const struct ssh_hash *hash;
     const void *extra;                 /* private to the kex methods */
 };
@@ -405,39 +530,48 @@ struct ssh_kexes {
     const struct ssh_kex *const *list;
 };
 
-struct ssh_signkey {
-    void *(*newkey) (const struct ssh_signkey *self,
-                     const char *data, int len);
-    void (*freekey) (void *key);
-    char *(*fmtkey) (void *key);
-    unsigned char *(*public_blob) (void *key, int *len);
-    unsigned char *(*private_blob) (void *key, int *len);
-    void *(*createkey) (const struct ssh_signkey *self,
-                        const unsigned char *pub_blob, int pub_len,
-			const unsigned char *priv_blob, int priv_len);
-    void *(*openssh_createkey) (const struct ssh_signkey *self,
-                                const unsigned char **blob, int *len);
-    int (*openssh_fmtkey) (void *key, unsigned char *blob, int len);
-    /* OpenSSH private key blobs, as created by openssh_fmtkey and
-     * consumed by openssh_createkey, always (at least so far...) take
-     * the form of a number of SSH-2 strings / mpints concatenated
-     * end-to-end. Because the new-style OpenSSH private key format
-     * stores those blobs without a containing string wrapper, we need
-     * to know how many strings each one consists of, so that we can
-     * skip over the right number to find the next key in the file.
-     * openssh_private_npieces gives that information. */
-    int openssh_private_npieces;
-    int (*pubkey_bits) (const struct ssh_signkey *self,
-                        const void *blob, int len);
-    int (*verifysig) (void *key, const char *sig, int siglen,
-		      const char *data, int datalen);
-    unsigned char *(*sign) (void *key, const char *data, int datalen,
-			    int *siglen);
-    const char *name;
-    const char *keytype;               /* for host key cache */
-    const void *extra;                 /* private to the public key methods */
+struct ssh_keyalg {
+    /* Constructors that create an ssh_key */
+    ssh_key *(*new_pub) (const ssh_keyalg *self, ptrlen pub);
+    ssh_key *(*new_priv) (const ssh_keyalg *self, ptrlen pub, ptrlen priv);
+    ssh_key *(*new_priv_openssh) (const ssh_keyalg *self, BinarySource *);
+
+    /* Methods that operate on an existing ssh_key */
+    void (*freekey) (ssh_key *key);
+    void (*sign) (ssh_key *key, const void *data, int datalen, BinarySink *);
+    int (*verify) (ssh_key *key, ptrlen sig, ptrlen data);
+    void (*public_blob)(ssh_key *key, BinarySink *);
+    void (*private_blob)(ssh_key *key, BinarySink *);
+    void (*openssh_blob) (ssh_key *key, BinarySink *);
+    char *(*cache_str) (ssh_key *key);
+
+    /* 'Class methods' that don't deal with an ssh_key at all */
+    int (*pubkey_bits) (const ssh_keyalg *self, ptrlen blob);
+
+    /* Constant data fields giving information about the key type */
+    const char *ssh_id;    /* string identifier in the SSH protocol */
+    const char *cache_id;  /* identifier used in PuTTY's host key cache */
+    const void *extra;     /* private to the public key methods */
 };
 
+#define ssh_key_new_pub(alg, data) ((alg)->new_pub(alg, data))
+#define ssh_key_new_priv(alg, pub, priv) ((alg)->new_priv(alg, pub, priv))
+#define ssh_key_new_priv_openssh(alg, bs) ((alg)->new_priv_openssh(alg, bs))
+
+#define ssh_key_free(key) ((*(key))->freekey(key))
+#define ssh_key_sign(key, data, len, bs) ((*(key))->sign(key, data, len, bs))
+#define ssh_key_verify(key, sig, data) ((*(key))->verify(key, sig, data))
+#define ssh_key_public_blob(key, bs) ((*(key))->public_blob(key, bs))
+#define ssh_key_private_blob(key, bs) ((*(key))->private_blob(key, bs))
+#define ssh_key_openssh_blob(key, bs) ((*(key))->openssh_blob(key, bs))
+#define ssh_key_cache_str(key) ((*(key))->cache_str(key))
+
+#define ssh_key_public_bits(alg, blob) ((alg)->pubkey_bits(alg, blob))
+
+#define ssh_key_alg(key) (*(key))
+#define ssh_key_ssh_id(key) ((*(key))->ssh_id)
+#define ssh_key_cache_id(key) ((*(key))->cache_id)
+
 struct ssh_compress {
     const char *name;
     /* For [email protected]: if non-NULL, this name will be considered once
@@ -445,19 +579,18 @@ struct ssh_compress {
     const char *delayed_name;
     void *(*compress_init) (void);
     void (*compress_cleanup) (void *);
-    int (*compress) (void *, unsigned char *block, int len,
-		     unsigned char **outblock, int *outlen);
+    void (*compress) (void *, unsigned char *block, int len,
+                      unsigned char **outblock, int *outlen,
+                      int minlen);
     void *(*decompress_init) (void);
     void (*decompress_cleanup) (void *);
     int (*decompress) (void *, unsigned char *block, int len,
 		       unsigned char **outblock, int *outlen);
-    int (*disable_compression) (void *);
     const char *text_name;
 };
 
 struct ssh2_userkey {
-    const struct ssh_signkey *alg;     /* the key algorithm */
-    void *data;			       /* the key data */
+    ssh_key *key;                      /* the key itself */
     char *comment;		       /* the key comment */
 };
 
@@ -480,14 +613,15 @@ extern const struct ssh_hash ssh_sha512;
 extern const struct ssh_kexes ssh_diffiehellman_group1;
 extern const struct ssh_kexes ssh_diffiehellman_group14;
 extern const struct ssh_kexes ssh_diffiehellman_gex;
+extern const struct ssh_kexes ssh_gssk5_sha1_kex;
 extern const struct ssh_kexes ssh_rsa_kex;
 extern const struct ssh_kexes ssh_ecdh_kex;
-extern const struct ssh_signkey ssh_dss;
-extern const struct ssh_signkey ssh_rsa;
-extern const struct ssh_signkey ssh_ecdsa_ed25519;
-extern const struct ssh_signkey ssh_ecdsa_nistp256;
-extern const struct ssh_signkey ssh_ecdsa_nistp384;
-extern const struct ssh_signkey ssh_ecdsa_nistp521;
+extern const ssh_keyalg ssh_dss;
+extern const ssh_keyalg ssh_rsa;
+extern const ssh_keyalg ssh_ecdsa_ed25519;
+extern const ssh_keyalg ssh_ecdsa_nistp256;
+extern const ssh_keyalg ssh_ecdsa_nistp384;
+extern const ssh_keyalg ssh_ecdsa_nistp521;
 extern const struct ssh_mac ssh_hmac_md5;
 extern const struct ssh_mac ssh_hmac_sha1;
 extern const struct ssh_mac ssh_hmac_sha1_buggy;
@@ -497,13 +631,13 @@ extern const struct ssh_mac ssh_hmac_sha256;
 
 void *aes_make_context(void);
 void aes_free_context(void *handle);
-void aes128_key(void *handle, unsigned char *key);
-void aes192_key(void *handle, unsigned char *key);
-void aes256_key(void *handle, unsigned char *key);
-void aes_iv(void *handle, unsigned char *iv);
-void aes_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len);
-void aes_ssh2_decrypt_blk(void *handle, unsigned char *blk, int len);
-void aes_ssh2_sdctr(void *handle, unsigned char *blk, int len);
+void aes128_key(void *handle, const void *key);
+void aes192_key(void *handle, const void *key);
+void aes256_key(void *handle, const void *key);
+void aes_iv(void *handle, const void *iv);
+void aes_ssh2_encrypt_blk(void *handle, void *blk, int len);
+void aes_ssh2_decrypt_blk(void *handle, void *blk, int len);
+void aes_ssh2_sdctr(void *handle, void *blk, int len);
 
 /*
  * PuTTY version number formatted as an SSH version string. 
@@ -521,8 +655,29 @@ extern
  */
 extern int ssh_fallback_cmd(void *handle);
 
-#ifndef MSCRYPTOAPI
 void SHATransform(word32 * digest, word32 * data);
+
+/*
+ * Check of compiler version
+ */
+#ifdef _FORCE_SHA_NI
+#   define COMPILER_SUPPORTS_SHA_NI
+#elif defined(__clang__)
+#   if __has_attribute(target) && __has_include(<shaintrin.h>) && (defined(__x86_64__) || defined(__i386))
+#       define COMPILER_SUPPORTS_SHA_NI
+#   endif
+#elif defined(__GNUC__)
+#    if ((__GNUC__ >= 5) && (defined(__x86_64__) || defined(__i386)))
+#       define COMPILER_SUPPORTS_SHA_NI
+#    endif
+#elif defined (_MSC_VER)
+#   if (defined(_M_X64) || defined(_M_IX86)) && _MSC_VER >= 1900
+#      define COMPILER_SUPPORTS_SHA_NI
+#   endif
+#endif
+
+#ifdef _FORCE_SOFTWARE_SHA
+#   undef COMPILER_SUPPORTS_SHA_NI
 #endif
 
 int random_byte(void);
@@ -542,7 +697,7 @@ void ssh_send_port_open(void *channel, const char *hostname, int port,
 extern char *pfd_connect(struct PortForwarding **pf, char *hostname, int port,
                          void *c, Conf *conf, int addressfamily);
 extern void pfd_close(struct PortForwarding *);
-extern int pfd_send(struct PortForwarding *, char *data, int len);
+extern int pfd_send(struct PortForwarding *, const void *data, int len);
 extern void pfd_send_eof(struct PortForwarding *);
 extern void pfd_confirm(struct PortForwarding *);
 extern void pfd_unthrottle(struct PortForwarding *);
@@ -622,7 +777,7 @@ void x11_free_fake_auth(struct X11FakeAuth *auth);
 struct X11Connection;                  /* opaque outside x11fwd.c */
 struct X11Connection *x11_init(tree234 *authtree, void *, const char *, int);
 extern void x11_close(struct X11Connection *);
-extern int x11_send(struct X11Connection *, char *, int);
+extern int x11_send(struct X11Connection *, const void *, int);
 extern void x11_send_eof(struct X11Connection *s);
 extern void x11_unthrottle(struct X11Connection *s);
 extern void x11_override_throttle(struct X11Connection *s, int enable);
@@ -652,8 +807,8 @@ char *platform_get_x_display(void);
  */
 void x11_get_auth_from_authfile(struct X11Display *display,
 				const char *authfilename);
-int x11_identify_auth_proto(const char *proto);
-void *x11_dehexify(const char *hex, int *outlen);
+int x11_identify_auth_proto(ptrlen protoname);
+void *x11_dehexify(ptrlen hex, int *outlen);
 
 Bignum copybn(Bignum b);
 Bignum bn_power_2(int n);
@@ -665,17 +820,13 @@ Bignum modmul(Bignum a, Bignum b, Bignum mod);
 Bignum modsub(const Bignum a, const Bignum b, const Bignum n);
 void decbn(Bignum n);
 extern Bignum Zero, One;
-Bignum bignum_from_bytes(const unsigned char *data, int nbytes);
-Bignum bignum_from_bytes_le(const unsigned char *data, int nbytes);
+Bignum bignum_from_bytes(const void *data, int nbytes);
+Bignum bignum_from_bytes_le(const void *data, int nbytes);
 Bignum bignum_random_in_range(const Bignum lower, const Bignum upper);
-int ssh1_read_bignum(const unsigned char *data, int len, Bignum * result);
 int bignum_bitcount(Bignum bn);
-int ssh1_bignum_length(Bignum bn);
-int ssh2_bignum_length(Bignum bn);
 int bignum_byte(Bignum bn, int i);
 int bignum_bit(Bignum bn, int i);
 void bignum_set_bit(Bignum bn, int i, int value);
-int ssh1_write_bignum(void *data, Bignum bn);
 Bignum biggcd(Bignum a, Bignum b);
 unsigned short bignum_mod_short(Bignum number, unsigned short modulus);
 Bignum bignum_add_long(Bignum number, unsigned long addend);
@@ -693,6 +844,11 @@ int bignum_cmp(Bignum a, Bignum b);
 char *bignum_decimal(Bignum x);
 Bignum bignum_from_decimal(const char *decimal);
 
+void BinarySink_put_mp_ssh1(BinarySink *, Bignum);
+void BinarySink_put_mp_ssh2(BinarySink *, Bignum);
+Bignum BinarySource_get_mp_ssh1(BinarySource *);
+Bignum BinarySource_get_mp_ssh2(BinarySource *);
+
 #ifdef DEBUG
 void diagbn(char *prefix, Bignum md);
 #endif
@@ -705,13 +861,13 @@ Bignum dh_create_e(void *, int nbits);
 const char *dh_validate_f(void *handle, Bignum f);
 Bignum dh_find_K(void *, Bignum f);
 
-int loadrsakey(const Filename *filename, struct RSAKey *key,
-	       const char *passphrase, const char **errorstr);
-int rsakey_encrypted(const Filename *filename, char **comment);
-int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen,
-		   char **commentptr, const char **errorstr);
-
-int saversakey(const Filename *filename, struct RSAKey *key, char *passphrase);
+int rsa_ssh1_encrypted(const Filename *filename, char **comment);
+int rsa_ssh1_loadpub(const Filename *filename, BinarySink *bs,
+                     char **commentptr, const char **errorstr);
+int rsa_ssh1_loadkey(const Filename *filename, struct RSAKey *key,
+                     const char *passphrase, const char **errorstr);
+int rsa_ssh1_savekey(const Filename *filename, struct RSAKey *key,
+                     char *passphrase);
 
 extern int base64_decode_atom(const char *atom, unsigned char *out);
 extern int base64_lines(int datalen);
@@ -727,13 +883,13 @@ int ssh2_userkey_encrypted(const Filename *filename, char **comment);
 struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
 				       const char *passphrase,
                                        const char **errorstr);
-unsigned char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
-				    int *pub_blob_len, char **commentptr,
-				    const char **errorstr);
+int ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
+                         BinarySink *bs,
+                         char **commentptr, const char **errorstr);
 int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key,
 		      char *passphrase);
-const struct ssh_signkey *find_pubkey_alg(const char *name);
-const struct ssh_signkey *find_pubkey_alg_len(int namelen, const char *name);
+const ssh_keyalg *find_pubkey_alg(const char *name);
+const ssh_keyalg *find_pubkey_alg_len(ptrlen name);
 
 enum {
     SSH_KEYTYPE_UNOPENABLE,
@@ -785,13 +941,13 @@ void ssh2_write_pubkey(FILE *fp, const char *comment,
                        const void *v_pub_blob, int pub_len,
                        int keytype);
 char *ssh2_fingerprint_blob(const void *blob, int bloblen);
-char *ssh2_fingerprint(const struct ssh_signkey *alg, void *data);
+char *ssh2_fingerprint(ssh_key *key);
 int key_type(const Filename *filename);
 const char *key_type_to_str(int type);
 #ifdef MPEXT
-unsigned char *openssh_loadpub_line(char * line, char **algorithm,
-                                    int *pub_blob_len, char **commentptr,
-                                    const char **errorstr);
+int openssh_loadpub_line(char * line, char **algorithm,
+                         BinarySink *bs,
+                         char **commentptr, const char **errorstr);
 #endif
 
 int import_possible(int type);
@@ -806,21 +962,17 @@ int export_ssh1(const Filename *filename, int type,
 int export_ssh2(const Filename *filename, int type,
                 struct ssh2_userkey *key, char *passphrase);
 
-void des3_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len);
-void des3_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len);
-void des3_decrypt_pubkey_ossh(unsigned char *key, unsigned char *iv,
-			      unsigned char *blk, int len);
-void des3_encrypt_pubkey_ossh(unsigned char *key, unsigned char *iv,
-			      unsigned char *blk, int len);
-void aes256_encrypt_pubkey(unsigned char *key, unsigned char *blk,
-			   int len);
-void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk,
-			   int len);
-
-void des_encrypt_xdmauth(const unsigned char *key,
-                         unsigned char *blk, int len);
-void des_decrypt_xdmauth(const unsigned char *key,
-                         unsigned char *blk, int len);
+void des3_decrypt_pubkey(const void *key, void *blk, int len);
+void des3_encrypt_pubkey(const void *key, void *blk, int len);
+void des3_decrypt_pubkey_ossh(const void *key, const void *iv,
+			      void *blk, int len);
+void des3_encrypt_pubkey_ossh(const void *key, const void *iv,
+			      void *blk, int len);
+void aes256_encrypt_pubkey(const void *key, void *blk, int len);
+void aes256_decrypt_pubkey(const void *key, void *blk, int len);
+
+void des_encrypt_xdmauth(const void *key, void *blk, int len);
+void des_decrypt_xdmauth(const void *key, void *blk, int len);
 
 void openssh_bcrypt(const char *passphrase,
                     const unsigned char *salt, int saltbytes,
@@ -857,8 +1009,8 @@ void *zlib_compress_init(void);
 void zlib_compress_cleanup(void *);
 void *zlib_decompress_init(void);
 void zlib_decompress_cleanup(void *);
-int zlib_compress_block(void *, unsigned char *block, int len,
-			unsigned char **outblock, int *outlen);
+void zlib_compress_block(void *, unsigned char *block, int len,
+                         unsigned char **outblock, int *outlen, int minlen);
 int zlib_decompress_block(void *, unsigned char *block, int len,
 			  unsigned char **outblock, int *outlen);
 
@@ -952,6 +1104,13 @@ void platform_ssh_share_cleanup(const char *name);
 #define SSH2_MSG_KEX_DH_GEX_GROUP                 31	/* 0x1f */
 #define SSH2_MSG_KEX_DH_GEX_INIT                  32	/* 0x20 */
 #define SSH2_MSG_KEX_DH_GEX_REPLY                 33	/* 0x21 */
+#define SSH2_MSG_KEXGSS_INIT                      30	/* 0x1e */
+#define SSH2_MSG_KEXGSS_CONTINUE                  31	/* 0x1f */
+#define SSH2_MSG_KEXGSS_COMPLETE                  32	/* 0x20 */
+#define SSH2_MSG_KEXGSS_HOSTKEY                   33	/* 0x21 */
+#define SSH2_MSG_KEXGSS_ERROR                     34	/* 0x22 */
+#define SSH2_MSG_KEXGSS_GROUPREQ                  40	/* 0x28 */
+#define SSH2_MSG_KEXGSS_GROUP                     41	/* 0x29 */
 #define SSH2_MSG_KEXRSA_PUBKEY                    30    /* 0x1e */
 #define SSH2_MSG_KEXRSA_SECRET                    31    /* 0x1f */
 #define SSH2_MSG_KEXRSA_DONE                      32    /* 0x20 */
@@ -988,6 +1147,13 @@ void platform_ssh_share_cleanup(const char *name);
 #define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK                 65
 #define SSH2_MSG_USERAUTH_GSSAPI_MIC                    66
 
+/* Virtual packet type, for packets too short to even have a type */
+#define SSH_MSG_NO_TYPE_CODE                  0x100
+
+/* Given that virtual packet types exist, this is how big the dispatch
+ * table has to be */
+#define SSH_MAX_MSG                           0x101
+
 /*
  * SSH-1 agent messages.
  */
@@ -1042,6 +1208,9 @@ void platform_ssh_share_cleanup(const char *name);
 
 #define SSH2_EXTENDED_DATA_STDERR                 1	/* 0x1 */
 
+const char *ssh1_pkt_type(int type);
+const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type);
+
 /*
  * Need this to warn about support for the original SSH-2 keyfile
  * format.

+ 294 - 0
source/putty/ssh1bpp.c

@@ -0,0 +1,294 @@
+/*
+ * Binary packet protocol for SSH-1.
+ */
+
+#include <assert.h>
+
+#include "putty.h"
+#include "ssh.h"
+#include "sshbpp.h"
+#include "sshcr.h"
+
+struct ssh1_bpp_state {
+    int crState;
+    long len, pad, biglen, length, maxlen;
+    unsigned char *data;
+    unsigned long realcrc, gotcrc;
+    int chunk;
+    PktIn *pktin;
+
+    const struct ssh_cipher *cipher;
+    void *cipher_ctx;
+
+    void *crcda_ctx;
+
+    void *compctx, *decompctx;
+
+    BinaryPacketProtocol bpp;
+};
+
+static void ssh1_bpp_free(BinaryPacketProtocol *bpp);
+static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp);
+static PktOut *ssh1_bpp_new_pktout(int type);
+static void ssh1_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt);
+
+const struct BinaryPacketProtocolVtable ssh1_bpp_vtable = {
+    ssh1_bpp_free,
+    ssh1_bpp_handle_input,
+    ssh1_bpp_new_pktout,
+    ssh1_bpp_format_packet,
+};
+
+BinaryPacketProtocol *ssh1_bpp_new(void)
+{
+    struct ssh1_bpp_state *s = snew(struct ssh1_bpp_state);
+    memset(s, 0, sizeof(*s));
+    s->bpp.vt = &ssh1_bpp_vtable;
+    return &s->bpp;
+}
+
+static void ssh1_bpp_free(BinaryPacketProtocol *bpp)
+{
+    struct ssh1_bpp_state *s = FROMFIELD(bpp, struct ssh1_bpp_state, bpp);
+    if (s->cipher)
+        s->cipher->free_context(s->cipher_ctx);
+    if (s->compctx)
+        zlib_compress_cleanup(s->compctx);
+    if (s->decompctx)
+        zlib_decompress_cleanup(s->decompctx);
+    if (s->crcda_ctx)
+        crcda_free_context(s->crcda_ctx);
+    if (s->pktin)
+        ssh_unref_packet(s->pktin);
+    sfree(s);
+}
+
+void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
+                         const struct ssh_cipher *cipher,
+                         const void *session_key)
+{
+    struct ssh1_bpp_state *s;
+    assert(bpp->vt == &ssh1_bpp_vtable);
+    s = FROMFIELD(bpp, struct ssh1_bpp_state, bpp);
+
+    assert(!s->cipher);
+
+    s->cipher = cipher;
+    if (s->cipher) {
+        s->cipher_ctx = cipher->make_context();
+        cipher->sesskey(s->cipher_ctx, session_key);
+
+        assert(!s->crcda_ctx);
+        s->crcda_ctx = crcda_make_context();
+    }
+}
+
+void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp)
+{
+    struct ssh1_bpp_state *s;
+    assert(bpp->vt == &ssh1_bpp_vtable);
+    s = FROMFIELD(bpp, struct ssh1_bpp_state, bpp);
+
+    assert(!s->compctx);
+    assert(!s->decompctx);
+
+    s->compctx = zlib_compress_init();
+    s->decompctx = zlib_decompress_init();
+}
+
+static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
+{
+    struct ssh1_bpp_state *s = FROMFIELD(bpp, struct ssh1_bpp_state, bpp);
+
+    crBegin(s->crState);
+
+    while (1) {
+        s->maxlen = 0;
+        s->length = 0;
+
+        {
+            unsigned char lenbuf[4];
+            crMaybeWaitUntilV(bufchain_try_fetch_consume(
+                                  bpp->in_raw, lenbuf, 4));
+            s->len = toint(GET_32BIT_MSB_FIRST(lenbuf));
+        }
+
+        if (s->len < 0 || s->len > 262144) { /* SSH1.5-mandated max size */
+            s->bpp.error = dupprintf(
+                "Extremely large packet length from server suggests"
+                " data stream corruption");
+            crStopV;
+        }
+
+        s->pad = 8 - (s->len % 8);
+        s->biglen = s->len + s->pad;
+        s->length = s->len - 5;
+
+        /*
+         * Allocate the packet to return, now we know its length.
+         */
+        s->pktin = snew_plus(PktIn, s->biglen);
+        s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
+        s->pktin->refcount = 1;
+        s->pktin->type = 0;
+
+        s->maxlen = s->biglen;
+        s->data = snew_plus_get_aux(s->pktin);
+
+        crMaybeWaitUntilV(bufchain_try_fetch_consume(
+                              bpp->in_raw, s->data, s->biglen));
+
+        if (s->cipher && detect_attack(s->crcda_ctx,
+                                       s->data, s->biglen, NULL)) {
+            s->bpp.error = dupprintf(
+                "Network attack (CRC compensation) detected!");
+            crStopV;
+        }
+
+        if (s->cipher)
+            s->cipher->decrypt(s->cipher_ctx, s->data, s->biglen);
+
+        s->realcrc = crc32_compute(s->data, s->biglen - 4);
+        s->gotcrc = GET_32BIT(s->data + s->biglen - 4);
+        if (s->gotcrc != s->realcrc) {
+            s->bpp.error = dupprintf(
+                "Incorrect CRC received on packet");
+            crStopV;
+        }
+
+        if (s->decompctx) {
+            unsigned char *decompblk;
+            int decomplen;
+            if (!zlib_decompress_block(s->decompctx,
+                                       s->data + s->pad, s->length + 1,
+                                       &decompblk, &decomplen)) {
+                s->bpp.error = dupprintf(
+                    "Zlib decompression encountered invalid data");
+                crStopV;
+            }
+
+            if (s->maxlen < s->pad + decomplen) {
+                PktIn *old_pktin = s->pktin;
+
+                s->maxlen = s->pad + decomplen;
+                s->pktin = snew_plus(PktIn, s->maxlen);
+                *s->pktin = *old_pktin; /* structure copy */
+                s->data = snew_plus_get_aux(s->pktin);
+
+                smemclr(old_pktin, s->biglen);
+                sfree(old_pktin);
+            }
+
+            memcpy(s->data + s->pad, decompblk, decomplen);
+            sfree(decompblk);
+            s->length = decomplen - 1;
+        }
+
+        /*
+         * Now we can find the bounds of the semantic content of the
+         * packet, and the initial type byte.
+         */
+        s->data += s->pad;
+        s->pktin->type = *s->data++;
+        BinarySource_INIT(s->pktin, s->data, s->length);
+
+        if (s->bpp.logctx) {
+            logblank_t blanks[MAX_BLANKS];
+            int nblanks = ssh1_censor_packet(
+                s->bpp.pls, s->pktin->type, FALSE,
+                make_ptrlen(s->data, s->length), blanks);
+            log_packet(s->bpp.logctx, PKT_INCOMING, s->pktin->type,
+                       ssh1_pkt_type(s->pktin->type),
+                       get_ptr(s->pktin), get_avail(s->pktin), nblanks, blanks,
+                       NULL, 0, NULL);
+        }
+
+        pq_push(s->bpp.in_pq, s->pktin);
+
+        {
+            int type = s->pktin->type;
+            s->pktin = NULL;
+
+            if (type == SSH1_MSG_DISCONNECT)
+                s->bpp.seen_disconnect = TRUE;
+        }
+    }
+    crFinishV;
+}
+
+static PktOut *ssh1_bpp_new_pktout(int pkt_type)
+{
+    PktOut *pkt = ssh_new_packet();
+    pkt->length = 4 + 8;	    /* space for length + max padding */
+    put_byte(pkt, pkt_type);
+    pkt->prefix = pkt->length;
+    pkt->type = pkt_type;
+    pkt->downstream_id = 0;
+    pkt->additional_log_text = NULL;
+    return pkt;
+}
+
+static void ssh1_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
+{
+    struct ssh1_bpp_state *s = FROMFIELD(bpp, struct ssh1_bpp_state, bpp);
+    int pad, biglen, i, pktoffs;
+    unsigned long crc;
+    int len;
+
+    if (s->bpp.logctx) {
+        ptrlen pktdata = make_ptrlen(pkt->data + pkt->prefix,
+                                     pkt->length - pkt->prefix);
+        logblank_t blanks[MAX_BLANKS];
+        int nblanks = ssh1_censor_packet(
+            s->bpp.pls, pkt->type, TRUE, pktdata, blanks);
+        log_packet(s->bpp.logctx, PKT_OUTGOING, pkt->type,
+                   ssh1_pkt_type(pkt->type),
+                   pktdata.ptr, pktdata.len, nblanks, blanks,
+                   NULL, 0, NULL);
+    }
+
+    if (s->compctx) {
+        unsigned char *compblk;
+        int complen;
+        zlib_compress_block(s->compctx, pkt->data + 12, pkt->length - 12,
+                            &compblk, &complen, 0);
+        /* Replace the uncompressed packet data with the compressed
+         * version. */
+        pkt->length = 12;
+        put_data(pkt, compblk, complen);
+        sfree(compblk);
+    }
+
+    put_uint32(pkt, 0); /* space for CRC */
+    len = pkt->length - 4 - 8;  /* len(type+data+CRC) */
+    pad = 8 - (len % 8);
+    pktoffs = 8 - pad;
+    biglen = len + pad;         /* len(padding+type+data+CRC) */
+
+    for (i = pktoffs; i < 4+8; i++)
+        pkt->data[i] = random_byte();
+    crc = crc32_compute(pkt->data + pktoffs + 4,
+                        biglen - 4); /* all ex len */
+    PUT_32BIT(pkt->data + pktoffs + 4 + biglen - 4, crc);
+    PUT_32BIT(pkt->data + pktoffs, len);
+
+    if (s->cipher)
+        s->cipher->encrypt(s->cipher_ctx, pkt->data + pktoffs + 4, biglen);
+
+    bufchain_add(s->bpp.out_raw, pkt->data + pktoffs,
+                 biglen + 4); /* len(length+padding+type+data+CRC) */
+
+    ssh_free_pktout(pkt);
+}
+
+#ifdef MPEXT
+int ssh1_bpp_get_compressing(BinaryPacketProtocol *bpp)
+{
+    return FROMFIELD(bpp, struct ssh1_bpp_state, bpp)->compctx != NULL;
+}
+
+const struct ssh_cipher * ssh1_bpp_get_cipher(BinaryPacketProtocol *bpp)
+{
+    return FROMFIELD(bpp, struct ssh1_bpp_state, bpp)->cipher;
+}
+#endif

+ 76 - 0
source/putty/ssh1censor.c

@@ -0,0 +1,76 @@
+/*
+ * Packet-censoring code for SSH-1, used to identify sensitive fields
+ * like passwords so that the logging system can avoid writing them
+ * into log files.
+ */
+
+#include <assert.h>
+
+#include "putty.h"
+#include "ssh.h"
+
+int ssh1_censor_packet(
+    const PacketLogSettings *pls, int type, int sender_is_client,
+    ptrlen pkt, logblank_t *blanks)
+{
+    int nblanks = 0;
+    ptrlen str;
+    BinarySource src[1];
+
+    BinarySource_BARE_INIT(src, pkt.ptr, pkt.len);
+
+    if (pls->omit_data &&
+        (type == SSH1_SMSG_STDOUT_DATA ||
+         type == SSH1_SMSG_STDERR_DATA ||
+         type == SSH1_CMSG_STDIN_DATA ||
+         type == SSH1_MSG_CHANNEL_DATA)) {
+        /* "Session data" packets - omit the data string. */
+        if (type == SSH1_MSG_CHANNEL_DATA)
+            get_uint32(src);           /* skip channel id */
+        str = get_string(src);
+        if (!get_err(src)) {
+            assert(nblanks < MAX_BLANKS);
+            blanks[nblanks].offset = src->pos - str.len;
+            blanks[nblanks].type = PKTLOG_OMIT;
+            blanks[nblanks].len = str.len;
+            nblanks++;
+        }
+    }
+
+    if (sender_is_client && pls->omit_passwords) {
+        if (type == SSH1_CMSG_AUTH_PASSWORD ||
+            type == SSH1_CMSG_AUTH_TIS_RESPONSE ||
+            type == SSH1_CMSG_AUTH_CCARD_RESPONSE) {
+            /* If this is a password or similar packet, blank the
+             * password(s). */
+            assert(nblanks < MAX_BLANKS);
+            blanks[nblanks].offset = 0;
+            blanks[nblanks].len = pkt.len;
+            blanks[nblanks].type = PKTLOG_BLANK;
+            nblanks++;
+        } else if (type == SSH1_CMSG_X11_REQUEST_FORWARDING) {
+            /*
+             * If this is an X forwarding request packet, blank the
+             * fake auth data.
+             *
+             * Note that while we blank the X authentication data
+             * here, we don't take any special action to blank the
+             * start of an X11 channel, so using MIT-MAGIC-COOKIE-1
+             * and actually opening an X connection without having
+             * session blanking enabled is likely to leak your cookie
+             * into the log.
+             */
+            get_string(src);              /* skip protocol name */
+            str = get_string(src);
+            if (!get_err(src)) {
+                assert(nblanks < MAX_BLANKS);
+                blanks[nblanks].offset = src->pos - str.len;
+                blanks[nblanks].type = PKTLOG_BLANK;
+                blanks[nblanks].len = str.len;
+                nblanks++;
+            }
+        }
+    }
+
+    return nblanks;
+}

+ 162 - 0
source/putty/ssh2bpp-bare.c

@@ -0,0 +1,162 @@
+/*
+ * Trivial binary packet protocol for the 'bare' ssh-connection
+ * protocol used in PuTTY's SSH-2 connection sharing system.
+ */
+
+#include <assert.h>
+
+#include "putty.h"
+#include "ssh.h"
+#include "sshbpp.h"
+#include "sshcr.h"
+
+struct ssh2_bare_bpp_state {
+    int crState;
+    long packetlen, maxlen;
+    unsigned char *data;
+    unsigned long incoming_sequence, outgoing_sequence;
+    PktIn *pktin;
+
+    BinaryPacketProtocol bpp;
+};
+
+static void ssh2_bare_bpp_free(BinaryPacketProtocol *bpp);
+static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp);
+static PktOut *ssh2_bare_bpp_new_pktout(int type);
+static void ssh2_bare_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *);
+
+const struct BinaryPacketProtocolVtable ssh2_bare_bpp_vtable = {
+    ssh2_bare_bpp_free,
+    ssh2_bare_bpp_handle_input,
+    ssh2_bare_bpp_new_pktout,
+    ssh2_bare_bpp_format_packet,
+};
+
+BinaryPacketProtocol *ssh2_bare_bpp_new(void)
+{
+    struct ssh2_bare_bpp_state *s = snew(struct ssh2_bare_bpp_state);
+    memset(s, 0, sizeof(*s));
+    s->bpp.vt = &ssh2_bare_bpp_vtable;
+    return &s->bpp;
+}
+
+static void ssh2_bare_bpp_free(BinaryPacketProtocol *bpp)
+{
+    struct ssh2_bare_bpp_state *s =
+        FROMFIELD(bpp, struct ssh2_bare_bpp_state, bpp);
+    if (s->pktin)
+        ssh_unref_packet(s->pktin);
+    sfree(s);
+}
+
+static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp)
+{
+    struct ssh2_bare_bpp_state *s =
+        FROMFIELD(bpp, struct ssh2_bare_bpp_state, bpp);
+
+    crBegin(s->crState);
+
+    while (1) {
+        /* Read the length field. */
+        {
+            unsigned char lenbuf[4];
+            crMaybeWaitUntilV(bufchain_try_fetch_consume(
+                                  s->bpp.in_raw, lenbuf, 4));
+            s->packetlen = toint(GET_32BIT_MSB_FIRST(lenbuf));
+        }
+
+        if (s->packetlen <= 0 || s->packetlen >= (long)OUR_V2_PACKETLIMIT) {
+            s->bpp.error = dupstr("Invalid packet length received");
+            crStopV;
+        }
+
+        /*
+         * Allocate the packet to return, now we know its length.
+         */
+        s->pktin = snew_plus(PktIn, s->packetlen);
+        s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
+        s->maxlen = 0;
+        s->pktin->refcount = 1;
+        s->data = snew_plus_get_aux(s->pktin);
+
+        s->pktin->encrypted_len = s->packetlen;
+
+        s->pktin->sequence = s->incoming_sequence++;
+
+        /*
+         * Read the remainder of the packet.
+         */
+        crMaybeWaitUntilV(bufchain_try_fetch_consume(
+                              s->bpp.in_raw, s->data, s->packetlen));
+
+        /*
+         * The data we just read is precisely the initial type byte
+         * followed by the packet payload.
+         */
+        s->pktin->type = s->data[0];
+        s->data++;
+        s->packetlen--;
+        BinarySource_INIT(s->pktin, s->data, s->packetlen);
+
+        /*
+         * Log incoming packet, possibly omitting sensitive fields.
+         */
+        if (s->bpp.logctx) {
+            logblank_t blanks[MAX_BLANKS];
+            int nblanks = ssh2_censor_packet(
+                s->bpp.pls, s->pktin->type, FALSE,
+                make_ptrlen(s->data, s->packetlen), blanks);
+            log_packet(s->bpp.logctx, PKT_INCOMING, s->pktin->type,
+                       ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx,
+                                     s->pktin->type),
+                       get_ptr(s->pktin), get_avail(s->pktin), nblanks, blanks,
+                       &s->pktin->sequence, 0, NULL);
+        }
+
+        pq_push(s->bpp.in_pq, s->pktin);
+
+        {
+            int type = s->pktin->type;
+            s->pktin = NULL;
+
+            if (type == SSH2_MSG_DISCONNECT)
+                s->bpp.seen_disconnect = TRUE;
+        }
+    }
+    crFinishV;
+}
+
+static PktOut *ssh2_bare_bpp_new_pktout(int pkt_type)
+{
+    PktOut *pkt = ssh_new_packet();
+    pkt->length = 4; /* space for packet length */
+    pkt->type = pkt_type;
+    put_byte(pkt, pkt_type);
+    return pkt;
+}
+
+static void ssh2_bare_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
+{
+    struct ssh2_bare_bpp_state *s =
+        FROMFIELD(bpp, struct ssh2_bare_bpp_state, bpp);
+
+    if (s->bpp.logctx) {
+        ptrlen pktdata = make_ptrlen(pkt->data + 5, pkt->length - 5);
+        logblank_t blanks[MAX_BLANKS];
+        int nblanks = ssh2_censor_packet(
+            s->bpp.pls, pkt->type, TRUE, pktdata, blanks);
+        log_packet(s->bpp.logctx, PKT_OUTGOING, pkt->type,
+                   ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx,
+                                 pkt->type),
+                   pktdata.ptr, pktdata.len, nblanks, blanks,
+                   &s->outgoing_sequence,
+                   pkt->downstream_id, pkt->additional_log_text);
+    }
+
+    s->outgoing_sequence++;        /* only for diagnostics, really */
+
+    PUT_32BIT(pkt->data, pkt->length - 4);
+    bufchain_add(s->bpp.out_raw, pkt->data, pkt->length);
+
+    ssh_free_pktout(pkt);
+}

+ 702 - 0
source/putty/ssh2bpp.c

@@ -0,0 +1,702 @@
+/*
+ * Binary packet protocol for SSH-2.
+ */
+
+#include <assert.h>
+
+#include "putty.h"
+#include "ssh.h"
+#include "sshbpp.h"
+#include "sshcr.h"
+
+struct ssh2_bpp_direction {
+    unsigned long sequence;
+    const struct ssh2_cipher *cipher;
+    void *cipher_ctx;
+    const struct ssh_mac *mac;
+    int etm_mode;
+    void *mac_ctx;
+    const struct ssh_compress *comp;
+    void *comp_ctx;
+};
+
+struct ssh2_bpp_state {
+    int crState;
+    long len, pad, payload, packetlen, maclen, length, maxlen;
+    unsigned char *buf;
+    size_t bufsize;
+    unsigned char *data;
+    unsigned cipherblk;
+    PktIn *pktin;
+    BinarySink *sc_mac_bs;
+
+    struct ssh2_bpp_direction in, out;
+    int pending_newkeys;
+
+    BinaryPacketProtocol bpp;
+};
+
+static void ssh2_bpp_free(BinaryPacketProtocol *bpp);
+static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp);
+static PktOut *ssh2_bpp_new_pktout(int type);
+static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt);
+
+const struct BinaryPacketProtocolVtable ssh2_bpp_vtable = {
+    ssh2_bpp_free,
+    ssh2_bpp_handle_input,
+    ssh2_bpp_new_pktout,
+    ssh2_bpp_format_packet,
+};
+
+BinaryPacketProtocol *ssh2_bpp_new(void)
+{
+    struct ssh2_bpp_state *s = snew(struct ssh2_bpp_state);
+    memset(s, 0, sizeof(*s));
+    s->bpp.vt = &ssh2_bpp_vtable;
+    return &s->bpp;
+}
+
+static void ssh2_bpp_free(BinaryPacketProtocol *bpp)
+{
+    struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
+    sfree(s->buf);
+    if (s->out.cipher_ctx)
+        s->out.cipher->free_context(s->out.cipher_ctx);
+    if (s->out.mac_ctx)
+        s->out.mac->free_context(s->out.mac_ctx);
+    if (s->out.comp_ctx)
+        s->out.comp->compress_cleanup(s->out.comp_ctx);
+    if (s->in.cipher_ctx)
+        s->in.cipher->free_context(s->in.cipher_ctx);
+    if (s->in.mac_ctx)
+        s->in.mac->free_context(s->in.mac_ctx);
+    if (s->in.comp_ctx)
+        s->in.comp->decompress_cleanup(s->in.comp_ctx);
+    if (s->pktin)
+        ssh_unref_packet(s->pktin);
+    sfree(s);
+}
+
+void ssh2_bpp_new_outgoing_crypto(
+    BinaryPacketProtocol *bpp,
+    const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
+    const struct ssh_mac *mac, int etm_mode, const void *mac_key,
+    const struct ssh_compress *compression)
+{
+    struct ssh2_bpp_state *s;
+    assert(bpp->vt == &ssh2_bpp_vtable);
+    s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
+
+    if (s->out.cipher_ctx)
+        s->out.cipher->free_context(s->out.cipher_ctx);
+    if (s->out.mac_ctx)
+        s->out.mac->free_context(s->out.mac_ctx);
+    if (s->out.comp_ctx)
+        s->out.comp->compress_cleanup(s->out.comp_ctx);
+
+    s->out.cipher = cipher;
+    if (cipher) {
+        s->out.cipher_ctx = cipher->make_context();
+        cipher->setkey(s->out.cipher_ctx, ckey);
+        cipher->setiv(s->out.cipher_ctx, iv);
+    }
+    s->out.mac = mac;
+    s->out.etm_mode = etm_mode;
+    if (mac) {
+        s->out.mac_ctx = mac->make_context(s->out.cipher_ctx);
+        mac->setkey(s->out.mac_ctx, mac_key);
+    }
+
+    s->out.comp = compression;
+    /* out_comp is always non-NULL, because no compression is
+     * indicated by ssh_comp_none. So compress_init always exists, but
+     * it may return a null out_comp_ctx. */
+    s->out.comp_ctx = compression->compress_init();
+}
+
+void ssh2_bpp_new_incoming_crypto(
+    BinaryPacketProtocol *bpp,
+    const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
+    const struct ssh_mac *mac, int etm_mode, const void *mac_key,
+    const struct ssh_compress *compression)
+{
+    struct ssh2_bpp_state *s;
+    assert(bpp->vt == &ssh2_bpp_vtable);
+    s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
+
+    if (s->in.cipher_ctx)
+        s->in.cipher->free_context(s->in.cipher_ctx);
+    if (s->in.mac_ctx)
+        s->in.mac->free_context(s->in.mac_ctx);
+    if (s->in.comp_ctx)
+        s->in.comp->decompress_cleanup(s->in.comp_ctx);
+
+    s->in.cipher = cipher;
+    if (cipher) {
+        s->in.cipher_ctx = cipher->make_context();
+        cipher->setkey(s->in.cipher_ctx, ckey);
+        cipher->setiv(s->in.cipher_ctx, iv);
+    }
+    s->in.mac = mac;
+    s->in.etm_mode = etm_mode;
+    if (mac) {
+        s->in.mac_ctx = mac->make_context(s->in.cipher_ctx);
+        mac->setkey(s->in.mac_ctx, mac_key);
+    }
+
+    s->in.comp = compression;
+    /* in_comp is always non-NULL, because no compression is
+     * indicated by ssh_comp_none. So compress_init always exists, but
+     * it may return a null in_comp_ctx. */
+    s->in.comp_ctx = compression->decompress_init();
+
+    /* Clear the pending_newkeys flag, so that handle_input below will
+     * start consuming the input data again. */
+    s->pending_newkeys = FALSE;
+}
+
+static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
+{
+    struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
+
+    crBegin(s->crState);
+
+    while (1) {
+        s->maxlen = 0;
+        s->length = 0;
+        if (s->in.cipher)
+            s->cipherblk = s->in.cipher->blksize;
+        else
+            s->cipherblk = 8;
+        if (s->cipherblk < 8)
+            s->cipherblk = 8;
+        s->maclen = s->in.mac ? s->in.mac->len : 0;
+
+        if (s->in.cipher && (s->in.cipher->flags & SSH_CIPHER_IS_CBC) &&
+            s->in.mac && !s->in.etm_mode) {
+            /*
+             * When dealing with a CBC-mode cipher, we want to avoid the
+             * possibility of an attacker's tweaking the ciphertext stream
+             * so as to cause us to feed the same block to the block
+             * cipher more than once and thus leak information
+             * (VU#958563).  The way we do this is not to take any
+             * decisions on the basis of anything we've decrypted until
+             * we've verified it with a MAC.  That includes the packet
+             * length, so we just read data and check the MAC repeatedly,
+             * and when the MAC passes, see if the length we've got is
+             * plausible.
+             *
+             * This defence is unnecessary in OpenSSH ETM mode, because
+             * the whole point of ETM mode is that the attacker can't
+             * tweak the ciphertext stream at all without the MAC
+             * detecting it before we decrypt anything.
+             */
+
+            /*
+             * Make sure we have buffer space for a maximum-size packet.
+             */
+            unsigned buflimit = OUR_V2_PACKETLIMIT + s->maclen;
+            if (s->bufsize < buflimit) {
+                s->bufsize = buflimit;
+                s->buf = sresize(s->buf, s->bufsize, unsigned char);
+            }
+
+            /* Read an amount corresponding to the MAC. */
+            crMaybeWaitUntilV(bufchain_try_fetch_consume(
+                                  s->bpp.in_raw, s->buf, s->maclen));
+
+            s->packetlen = 0;
+            s->in.mac->start(s->in.mac_ctx);
+            s->sc_mac_bs = s->in.mac->sink(s->in.mac_ctx);
+            put_uint32(s->sc_mac_bs, s->in.sequence);
+
+            for (;;) { /* Once around this loop per cipher block. */
+                /* Read another cipher-block's worth, and tack it on to
+                 * the end. */
+                crMaybeWaitUntilV(bufchain_try_fetch_consume(
+                                      s->bpp.in_raw,
+                                      s->buf + (s->packetlen + s->maclen),
+                                      s->cipherblk));
+                /* Decrypt one more block (a little further back in
+                 * the stream). */
+                s->in.cipher->decrypt(
+                    s->in.cipher_ctx,
+                    s->buf + s->packetlen, s->cipherblk);
+
+                /* Feed that block to the MAC. */
+                put_data(s->sc_mac_bs,
+                         s->buf + s->packetlen, s->cipherblk);
+                s->packetlen += s->cipherblk;
+
+                /* See if that gives us a valid packet. */
+                if (s->in.mac->verresult(
+                        s->in.mac_ctx, s->buf + s->packetlen) &&
+                    ((s->len = toint(GET_32BIT(s->buf))) ==
+                     s->packetlen-4))
+                    break;
+                if (s->packetlen >= (long)OUR_V2_PACKETLIMIT) {
+                    s->bpp.error = dupprintf(
+                        "No valid incoming packet found");
+                    crStopV;
+                }
+            }
+            s->maxlen = s->packetlen + s->maclen;
+
+            /*
+             * Now transfer the data into an output packet.
+             */
+            s->pktin = snew_plus(PktIn, s->maxlen);
+            s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
+            s->pktin->refcount = 1;
+            s->pktin->type = 0;
+            s->data = snew_plus_get_aux(s->pktin);
+            memcpy(s->data, s->buf, s->maxlen);
+        } else if (s->in.mac && s->in.etm_mode) {
+            if (s->bufsize < 4) {
+                s->bufsize = 4;
+                s->buf = sresize(s->buf, s->bufsize, unsigned char);
+            }
+
+            /*
+             * OpenSSH encrypt-then-MAC mode: the packet length is
+             * unencrypted, unless the cipher supports length encryption.
+             */
+            crMaybeWaitUntilV(bufchain_try_fetch_consume(
+                                  s->bpp.in_raw, s->buf, 4));
+
+            /* Cipher supports length decryption, so do it */
+            if (s->in.cipher &&
+                (s->in.cipher->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
+                /* Keep the packet the same though, so the MAC passes */
+                unsigned char len[4];
+                memcpy(len, s->buf, 4);
+                s->in.cipher->decrypt_length(
+                    s->in.cipher_ctx, len, 4, s->in.sequence);
+                s->len = toint(GET_32BIT(len));
+            } else {
+                s->len = toint(GET_32BIT(s->buf));
+            }
+
+            /*
+             * _Completely_ silly lengths should be stomped on before they
+             * do us any more damage.
+             */
+            if (s->len < 0 || s->len > (long)OUR_V2_PACKETLIMIT ||
+                s->len % s->cipherblk != 0) {
+                s->bpp.error = dupprintf(
+                    "Incoming packet length field was garbled");
+                crStopV;
+            }
+
+            /*
+             * So now we can work out the total packet length.
+             */
+            s->packetlen = s->len + 4;
+
+            /*
+             * Allocate the packet to return, now we know its length.
+             */
+            s->pktin = snew_plus(PktIn, OUR_V2_PACKETLIMIT + s->maclen);
+            s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
+            s->pktin->refcount = 1;
+            s->pktin->type = 0;
+            s->data = snew_plus_get_aux(s->pktin);
+            memcpy(s->data, s->buf, 4);
+
+            /*
+             * Read the remainder of the packet.
+             */
+            crMaybeWaitUntilV(bufchain_try_fetch_consume(
+                                  s->bpp.in_raw, s->data + 4,
+                                  s->packetlen + s->maclen - 4));
+
+            /*
+             * Check the MAC.
+             */
+            if (s->in.mac && !s->in.mac->verify(
+                    s->in.mac_ctx, s->data, s->len + 4, s->in.sequence)) {
+                s->bpp.error = dupprintf("Incorrect MAC received on packet");
+                crStopV;
+            }
+
+            /* Decrypt everything between the length field and the MAC. */
+            if (s->in.cipher)
+                s->in.cipher->decrypt(
+                    s->in.cipher_ctx, s->data + 4, s->packetlen - 4);
+        } else {
+            if (s->bufsize < s->cipherblk) {
+                s->bufsize = s->cipherblk;
+                s->buf = sresize(s->buf, s->bufsize, unsigned char);
+            }
+
+            /*
+             * Acquire and decrypt the first block of the packet. This will
+             * contain the length and padding details.
+             */
+            crMaybeWaitUntilV(bufchain_try_fetch_consume(
+                                  s->bpp.in_raw, s->buf, s->cipherblk));
+
+            if (s->in.cipher)
+                s->in.cipher->decrypt(
+                    s->in.cipher_ctx, s->buf, s->cipherblk);
+
+            /*
+             * Now get the length figure.
+             */
+            s->len = toint(GET_32BIT(s->buf));
+
+            /*
+             * _Completely_ silly lengths should be stomped on before they
+             * do us any more damage.
+             */
+            if (s->len < 0 || s->len > (long)OUR_V2_PACKETLIMIT ||
+                (s->len + 4) % s->cipherblk != 0) {
+                s->bpp.error = dupprintf(
+                    "Incoming packet was garbled on decryption");
+                crStopV;
+            }
+
+            /*
+             * So now we can work out the total packet length.
+             */
+            s->packetlen = s->len + 4;
+
+            /*
+             * Allocate the packet to return, now we know its length.
+             */
+            s->maxlen = s->packetlen + s->maclen;
+            s->pktin = snew_plus(PktIn, s->maxlen);
+            s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
+            s->pktin->refcount = 1;
+            s->pktin->type = 0;
+            s->data = snew_plus_get_aux(s->pktin);
+            memcpy(s->data, s->buf, s->cipherblk);
+
+            /*
+             * Read and decrypt the remainder of the packet.
+             */
+            crMaybeWaitUntilV(bufchain_try_fetch_consume(
+                                  s->bpp.in_raw, s->data + s->cipherblk,
+                                  s->packetlen + s->maclen - s->cipherblk));
+
+            /* Decrypt everything _except_ the MAC. */
+            if (s->in.cipher)
+                s->in.cipher->decrypt(
+                    s->in.cipher_ctx,
+                    s->data + s->cipherblk, s->packetlen - s->cipherblk);
+
+            /*
+             * Check the MAC.
+             */
+            if (s->in.mac && !s->in.mac->verify(
+                    s->in.mac_ctx, s->data, s->len + 4, s->in.sequence)) {
+                s->bpp.error = dupprintf("Incorrect MAC received on packet");
+                crStopV;
+            }
+        }
+        /* Get and sanity-check the amount of random padding. */
+        s->pad = s->data[4];
+        if (s->pad < 4 || s->len - s->pad < 1) {
+            s->bpp.error = dupprintf(
+                "Invalid padding length on received packet");
+            crStopV;
+        }
+        /*
+         * This enables us to deduce the payload length.
+         */
+        s->payload = s->len - s->pad - 1;
+
+        s->length = s->payload + 5;
+        s->pktin->encrypted_len = s->packetlen;
+
+        s->pktin->sequence = s->in.sequence++;
+
+        s->length = s->packetlen - s->pad;
+        assert(s->length >= 0);
+
+        /*
+         * Decompress packet payload.
+         */
+        {
+            unsigned char *newpayload;
+            int newlen;
+            if (s->in.comp && s->in.comp->decompress(
+                    s->in.comp_ctx, s->data + 5, s->length - 5,
+                    &newpayload, &newlen)) {
+                if (s->maxlen < newlen + 5) {
+                    PktIn *old_pktin = s->pktin;
+
+                    s->maxlen = newlen + 5;
+                    s->pktin = snew_plus(PktIn, s->maxlen);
+                    *s->pktin = *old_pktin; /* structure copy */
+                    s->data = snew_plus_get_aux(s->pktin);
+
+                    smemclr(old_pktin, s->packetlen + s->maclen);
+                    sfree(old_pktin);
+                }
+                s->length = 5 + newlen;
+                memcpy(s->data + 5, newpayload, newlen);
+                sfree(newpayload);
+            }
+        }
+
+        /*
+         * Now we can identify the semantic content of the packet,
+         * and also the initial type byte.
+         */
+        if (s->length <= 5) { /* == 5 we hope, but robustness */
+            /*
+             * RFC 4253 doesn't explicitly say that completely empty
+             * packets with no type byte are forbidden. We handle them
+             * here by giving them a type code larger than 0xFF, which
+             * will be picked up at the next layer and trigger
+             * SSH_MSG_UNIMPLEMENTED.
+             */
+            s->pktin->type = SSH_MSG_NO_TYPE_CODE;
+            s->length = 0;
+            BinarySource_INIT(s->pktin, s->data + 5, 0);
+        } else {
+            s->pktin->type = s->data[5];
+            s->length -= 6;
+            BinarySource_INIT(s->pktin, s->data + 6, s->length);
+        }
+
+        if (s->bpp.logctx) {
+            logblank_t blanks[MAX_BLANKS];
+            int nblanks = ssh2_censor_packet(
+                s->bpp.pls, s->pktin->type, FALSE,
+                make_ptrlen(s->data, s->length), blanks);
+            log_packet(s->bpp.logctx, PKT_INCOMING, s->pktin->type,
+                       ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx,
+                                     s->pktin->type),
+                       get_ptr(s->pktin), get_avail(s->pktin), nblanks, blanks,
+                       &s->pktin->sequence, 0, NULL);
+        }
+
+        pq_push(s->bpp.in_pq, s->pktin);
+
+        {
+            int type = s->pktin->type;
+            s->pktin = NULL;
+
+            if (type == SSH2_MSG_DISCONNECT)
+                s->bpp.seen_disconnect = TRUE;
+            if (type == SSH2_MSG_NEWKEYS) {
+                /*
+                 * Mild layer violation: in this situation we must
+                 * suspend processing of the input byte stream until
+                 * the transport layer has initialised the new keys by
+                 * calling ssh2_bpp_new_incoming_crypto above.
+                 */
+                s->pending_newkeys = TRUE;
+                crWaitUntilV(!s->pending_newkeys);
+            }
+        }
+    }
+    crFinishV;
+}
+
+static PktOut *ssh2_bpp_new_pktout(int pkt_type)
+{
+    PktOut *pkt = ssh_new_packet();
+    pkt->length = 5; /* space for packet length + padding length */
+    pkt->minlen = 0;
+    pkt->type = pkt_type;
+    put_byte(pkt, pkt_type);
+    pkt->prefix = pkt->length;
+    return pkt;
+}
+
+static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
+{
+    int origlen, cipherblk, maclen, padding, unencrypted_prefix, i;
+
+    if (s->bpp.logctx) {
+        ptrlen pktdata = make_ptrlen(pkt->data + pkt->prefix,
+                                     pkt->length - pkt->prefix);
+        logblank_t blanks[MAX_BLANKS];
+        int nblanks = ssh2_censor_packet(
+            s->bpp.pls, pkt->type, TRUE, pktdata, blanks);
+        log_packet(s->bpp.logctx, PKT_OUTGOING, pkt->type,
+                   ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx,
+                                 pkt->type),
+                   pktdata.ptr, pktdata.len, nblanks, blanks, &s->out.sequence,
+                   pkt->downstream_id, pkt->additional_log_text);
+    }
+
+    cipherblk = s->out.cipher ? s->out.cipher->blksize : 8;
+    cipherblk = cipherblk < 8 ? 8 : cipherblk;  /* or 8 if blksize < 8 */
+
+    if (s->out.comp && s->out.comp_ctx) {
+        unsigned char *newpayload;
+        int minlen, newlen;
+
+        /*
+         * Compress packet payload.
+         */
+        minlen = pkt->minlen;
+        if (minlen) {
+            /*
+             * Work out how much compressed data we need (at least) to
+             * make the overall packet length come to pkt->minlen.
+             */
+            if (s->out.mac)
+                minlen -= s->out.mac->len;
+            minlen -= 8;              /* length field + min padding */
+        }
+
+        s->out.comp->compress(s->out.comp_ctx, pkt->data + 5, pkt->length - 5,
+                              &newpayload, &newlen, minlen);
+        pkt->length = 5;
+        put_data(pkt, newpayload, newlen);
+        sfree(newpayload);
+    }
+
+    /*
+     * Add padding. At least four bytes, and must also bring total
+     * length (minus MAC) up to a multiple of the block size.
+     * If pkt->forcepad is set, make sure the packet is at least that size
+     * after padding.
+     */
+    padding = 4;
+    unencrypted_prefix = (s->out.mac && s->out.etm_mode) ? 4 : 0;
+    padding +=
+        (cipherblk - (pkt->length - unencrypted_prefix + padding) % cipherblk)
+        % cipherblk;
+    assert(padding <= 255);
+    maclen = s->out.mac ? s->out.mac->len : 0;
+    origlen = pkt->length;
+    for (i = 0; i < padding; i++)
+        put_byte(pkt, random_byte());
+    pkt->data[4] = padding;
+    PUT_32BIT(pkt->data, origlen + padding - 4);
+
+    /* Encrypt length if the scheme requires it */
+    if (s->out.cipher &&
+        (s->out.cipher->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
+        s->out.cipher->encrypt_length(s->out.cipher_ctx, pkt->data, 4,
+                                      s->out.sequence);
+    }
+
+    put_padding(pkt, maclen, 0);
+
+    if (s->out.mac && s->out.etm_mode) {
+        /*
+         * OpenSSH-defined encrypt-then-MAC protocol.
+         */
+        if (s->out.cipher)
+            s->out.cipher->encrypt(s->out.cipher_ctx,
+                                   pkt->data + 4, origlen + padding - 4);
+        s->out.mac->generate(s->out.mac_ctx, pkt->data, origlen + padding,
+                             s->out.sequence);
+    } else {
+        /*
+         * SSH-2 standard protocol.
+         */
+        if (s->out.mac)
+            s->out.mac->generate(
+                s->out.mac_ctx, pkt->data, origlen + padding,
+                s->out.sequence);
+        if (s->out.cipher)
+            s->out.cipher->encrypt(s->out.cipher_ctx,
+                                   pkt->data, origlen + padding);
+    }
+
+    s->out.sequence++;       /* whether or not we MACed */
+    pkt->encrypted_len = origlen + padding;
+
+}
+
+static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
+{
+    struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
+
+    if (pkt->minlen > 0 && !(s->out.comp && s->out.comp_ctx)) {
+        /*
+         * If we've been told to pad the packet out to a given minimum
+         * length, but we're not compressing (and hence can't get the
+         * compression to do the padding by pointlessly opening and
+         * closing zlib blocks), then our other strategy is to precede
+         * this message with an SSH_MSG_IGNORE that makes it up to the
+         * right length.
+         *
+         * A third option in principle, and the most obviously
+         * sensible, would be to set the explicit padding field in the
+         * packet to more than its minimum value. Sadly, that turns
+         * out to break some servers (our institutional memory thinks
+         * Cisco in particular) and so we abandoned that idea shortly
+         * after trying it.
+         */
+
+        /*
+         * Calculate the length we expect the real packet to have.
+         */
+        int block, length;
+        PktOut *ignore_pkt;
+
+        block = s->out.cipher ? s->out.cipher->blksize : 0;
+        if (block < 8)
+            block = 8;
+        length = pkt->length;
+        length += 4;       /* minimum 4 byte padding */
+        length += block-1;
+        length -= (length % block);
+        if (s->out.mac)
+            length += s->out.mac->len;
+
+        if (length < pkt->minlen) {
+            /*
+             * We need an ignore message. Calculate its length.
+             */
+            length = pkt->minlen - length;
+
+            /*
+             * And work backwards from that to the length of the
+             * contained string.
+             */
+            if (s->out.mac)
+                length -= s->out.mac->len;
+            length -= 8;               /* length field + min padding */
+            length -= 5;               /* type code + string length prefix */
+
+            if (length < 0)
+                length = 0;
+
+            ignore_pkt = ssh2_bpp_new_pktout(SSH2_MSG_IGNORE);
+            put_uint32(ignore_pkt, length);
+            while (length-- > 0)
+                put_byte(ignore_pkt, random_byte());
+            ssh2_bpp_format_packet_inner(s, ignore_pkt);
+            bufchain_add(s->bpp.out_raw, ignore_pkt->data, ignore_pkt->length);
+            ssh_free_pktout(ignore_pkt);
+        }
+    }
+
+    ssh2_bpp_format_packet_inner(s, pkt);
+    bufchain_add(s->bpp.out_raw, pkt->data, pkt->length);
+
+    ssh_free_pktout(pkt);
+}
+
+#ifdef MPEXT
+const struct ssh2_cipher * ssh2_bpp_get_cscipher(BinaryPacketProtocol *bpp)
+{
+    return FROMFIELD(bpp, struct ssh2_bpp_state, bpp)->out.cipher;
+}
+
+const struct ssh2_cipher * ssh2_bpp_get_sccipher(BinaryPacketProtocol *bpp)
+{
+    return FROMFIELD(bpp, struct ssh2_bpp_state, bpp)->in.cipher;
+}
+
+const struct ssh_compress * ssh2_bpp_get_cscomp(BinaryPacketProtocol *bpp)
+{
+    return FROMFIELD(bpp, struct ssh2_bpp_state, bpp)->out.comp;
+}
+
+const struct ssh_compress * ssh2_bpp_get_sccomp(BinaryPacketProtocol *bpp)
+{
+    return FROMFIELD(bpp, struct ssh2_bpp_state, bpp)->in.comp;
+}
+
+#endif

+ 107 - 0
source/putty/ssh2censor.c

@@ -0,0 +1,107 @@
+/*
+ * Packet-censoring code for SSH-2, used to identify sensitive fields
+ * like passwords so that the logging system can avoid writing them
+ * into log files.
+ */
+
+#include <assert.h>
+
+#include "putty.h"
+#include "ssh.h"
+
+int ssh2_censor_packet(
+    const PacketLogSettings *pls, int type, int sender_is_client,
+    ptrlen pkt, logblank_t *blanks)
+{
+    int nblanks = 0;
+    ptrlen str;
+    BinarySource src[1];
+
+    BinarySource_BARE_INIT(src, pkt.ptr, pkt.len);
+
+    if (pls->omit_data &&
+        (type == SSH2_MSG_CHANNEL_DATA ||
+         type == SSH2_MSG_CHANNEL_EXTENDED_DATA)) {
+        /* "Session data" packets - omit the data string. */
+        get_uint32(src);              /* skip channel id */
+        if (type == SSH2_MSG_CHANNEL_EXTENDED_DATA)
+            get_uint32(src);          /* skip extended data type */
+        str = get_string(src);
+        if (!get_err(src)) {
+            assert(nblanks < MAX_BLANKS);
+            blanks[nblanks].offset = src->pos - str.len;
+            blanks[nblanks].type = PKTLOG_OMIT;
+            blanks[nblanks].len = str.len;
+            nblanks++;
+        }
+    }
+
+    if (sender_is_client && pls->omit_passwords) {
+        if (type == SSH2_MSG_USERAUTH_REQUEST) {
+            /* If this is a password packet, blank the password(s). */
+            get_string(src);              /* username */
+            get_string(src);              /* service name */
+            str = get_string(src);        /* auth method */
+            if (ptrlen_eq_string(str, "password")) {
+                get_bool(src);
+                /* Blank the password field. */
+                str = get_string(src);
+                if (!get_err(src)) {
+                    assert(nblanks < MAX_BLANKS);
+                    blanks[nblanks].offset = src->pos - str.len;
+                    blanks[nblanks].type = PKTLOG_BLANK;
+                    blanks[nblanks].len = str.len;
+                    nblanks++;
+                    /* If there's another password field beyond it
+                     * (change of password), blank that too. */
+                    str = get_string(src);
+                    if (!get_err(src))
+                        blanks[nblanks-1].len =
+                            src->pos - blanks[nblanks].offset;
+                }
+            }
+        } else if (pls->actx == SSH2_PKTCTX_KBDINTER &&
+                   type == SSH2_MSG_USERAUTH_INFO_RESPONSE) {
+            /* If this is a keyboard-interactive response packet,
+             * blank the responses. */
+            get_uint32(src);
+            assert(nblanks < MAX_BLANKS);
+            blanks[nblanks].offset = src->pos;
+            blanks[nblanks].type = PKTLOG_BLANK;
+            do {
+                str = get_string(src);
+            } while (!get_err(src));
+            blanks[nblanks].len = src->pos - blanks[nblanks].offset;
+            nblanks++;
+        } else if (type == SSH2_MSG_CHANNEL_REQUEST) {
+            /*
+             * If this is an X forwarding request packet, blank the
+             * fake auth data.
+             *
+             * Note that while we blank the X authentication data
+             * here, we don't take any special action to blank the
+             * start of an X11 channel, so using MIT-MAGIC-COOKIE-1
+             * and actually opening an X connection without having
+             * session blanking enabled is likely to leak your cookie
+             * into the log.
+             */
+            get_uint32(src);
+            str = get_string(src);
+            if (ptrlen_eq_string(str, "x11-req")) {
+                get_bool(src);
+                get_bool(src);
+                get_string(src);
+                str = get_string(src);
+                if (!get_err(src)) {
+                    assert(nblanks < MAX_BLANKS);
+                    blanks[nblanks].offset = src->pos - str.len;
+                    blanks[nblanks].type = PKTLOG_BLANK;
+                    blanks[nblanks].len = str.len;
+                    nblanks++;
+                }
+            }
+        }
+    }
+
+    return nblanks;
+}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 841 - 388
source/putty/sshaes.c


+ 5 - 4
source/putty/ssharcf.c

@@ -11,8 +11,9 @@ typedef struct {
     unsigned char i, j, s[256];
 } ArcfourContext;
 
-static void arcfour_block(void *handle, unsigned char *blk, int len)
+static void arcfour_block(void *handle, void *vblk, int len)
 {
+    unsigned char *blk = (unsigned char *)vblk;
     ArcfourContext *ctx = (ArcfourContext *)handle;
     unsigned k;
     unsigned char tmp, i, j, *s;
@@ -79,21 +80,21 @@ static void arcfour_stir(ArcfourContext *ctx)
     sfree(junk);
 }
 
-static void arcfour128_key(void *handle, unsigned char *key)
+static void arcfour128_key(void *handle, const void *key)
 {
     ArcfourContext *ctx = (ArcfourContext *)handle;
     arcfour_setkey(ctx, key, 16);
     arcfour_stir(ctx);
 }
 
-static void arcfour256_key(void *handle, unsigned char *key)
+static void arcfour256_key(void *handle, const void *key)
 {
     ArcfourContext *ctx = (ArcfourContext *)handle;
     arcfour_setkey(ctx, key, 32);
     arcfour_stir(ctx);
 }
 
-static void arcfour_iv(void *handle, unsigned char *key)
+static void arcfour_iv(void *handle, const void *iv)
 {
 
 }

+ 3 - 6
source/putty/sshbcrypt.c

@@ -56,16 +56,13 @@ void bcrypt_genblock(int counter,
 {
     SHA512_State shastate;
     unsigned char hashed_salt[64];
-    unsigned char countbuf[4];
 
     /* Hash the input salt with the counter value optionally suffixed
      * to get our real 32-byte salt */
     SHA512_Init(&shastate);
-    SHA512_Bytes(&shastate, salt, saltbytes);
-    if (counter) {
-        PUT_32BIT_MSB_FIRST(countbuf, counter);
-        SHA512_Bytes(&shastate, countbuf, 4);
-    }
+    put_data(&shastate, salt, saltbytes);
+    if (counter)
+        put_uint32(&shastate, counter);
     SHA512_Final(&shastate, hashed_salt);
 
     bcrypt_hash(hashed_passphrase, 64, hashed_salt, 64, output);

+ 19 - 21
source/putty/sshblowf.c

@@ -300,7 +300,7 @@ static void blowfish_decrypt(word32 xL, word32 xR, word32 * output,
 }
 
 static void blowfish_lsb_encrypt_cbc(unsigned char *blk, int len,
-				     BlowfishContext * ctx)
+                                     BlowfishContext * ctx)
 {
     word32 xL, xR, out[2], iv0, iv1;
 
@@ -327,9 +327,9 @@ static void blowfish_lsb_encrypt_cbc(unsigned char *blk, int len,
     ctx->iv1 = iv1;
 }
 
-void blowfish_lsb_encrypt_ecb(unsigned char *blk, int len,
-                              BlowfishContext * ctx)
+void blowfish_lsb_encrypt_ecb(void *vblk, int len, BlowfishContext * ctx)
 {
+    unsigned char *blk = (unsigned char *)vblk;
     word32 xL, xR, out[2];
 
     assert((len & 7) == 0);
@@ -472,9 +472,11 @@ void blowfish_initkey(BlowfishContext *ctx)
 }
 
 void blowfish_expandkey(BlowfishContext * ctx,
-                        const unsigned char *key, short keybytes,
-                        const unsigned char *salt, short saltbytes)
+                        const void *vkey, short keybytes,
+                        const void *vsalt, short saltbytes)
 {
+    const unsigned char *key = (const unsigned char *)vkey;
+    const unsigned char *salt = (const unsigned char *)vsalt;
     word32 *S0 = ctx->S0;
     word32 *S1 = ctx->S1;
     word32 *S2 = ctx->S2;
@@ -570,26 +572,27 @@ void blowfish_free_context(void *handle)
     sfree(handle);
 }
 
-static void blowfish_key(void *handle, unsigned char *key)
+static void blowfish_key(void *handle, const void *key)
 {
     BlowfishContext *ctx = (BlowfishContext *)handle;
     blowfish_setkey(ctx, key, 16);
 }
 
-static void blowfish256_key(void *handle, unsigned char *key)
+static void blowfish256_key(void *handle, const void *key)
 {
     BlowfishContext *ctx = (BlowfishContext *)handle;
     blowfish_setkey(ctx, key, 32);
 }
 
-static void blowfish_iv(void *handle, unsigned char *key)
+static void blowfish_iv(void *handle, const void *viv)
 {
+    const unsigned char *iv = (const unsigned char *)viv;
     BlowfishContext *ctx = (BlowfishContext *)handle;
-    ctx->iv0 = GET_32BIT_MSB_FIRST(key);
-    ctx->iv1 = GET_32BIT_MSB_FIRST(key + 4);
+    ctx->iv0 = GET_32BIT_MSB_FIRST(iv);
+    ctx->iv1 = GET_32BIT_MSB_FIRST(iv + 4);
 }
 
-static void blowfish_sesskey(void *handle, unsigned char *key)
+static void blowfish_sesskey(void *handle, const void *key)
 {
     BlowfishContext *ctx = (BlowfishContext *)handle;
     blowfish_setkey(ctx, key, SSH_SESSION_KEY_LENGTH);
@@ -598,36 +601,31 @@ static void blowfish_sesskey(void *handle, unsigned char *key)
     ctx[1] = ctx[0];		       /* structure copy */
 }
 
-static void blowfish_ssh1_encrypt_blk(void *handle, unsigned char *blk,
-				      int len)
+static void blowfish_ssh1_encrypt_blk(void *handle, void *blk, int len)
 {
     BlowfishContext *ctx = (BlowfishContext *)handle;
     blowfish_lsb_encrypt_cbc(blk, len, ctx);
 }
 
-static void blowfish_ssh1_decrypt_blk(void *handle, unsigned char *blk,
-				      int len)
+static void blowfish_ssh1_decrypt_blk(void *handle, void *blk, int len)
 {
     BlowfishContext *ctx = (BlowfishContext *)handle;
     blowfish_lsb_decrypt_cbc(blk, len, ctx+1);
 }
 
-static void blowfish_ssh2_encrypt_blk(void *handle, unsigned char *blk,
-				      int len)
+static void blowfish_ssh2_encrypt_blk(void *handle, void *blk, int len)
 {
     BlowfishContext *ctx = (BlowfishContext *)handle;
     blowfish_msb_encrypt_cbc(blk, len, ctx);
 }
 
-static void blowfish_ssh2_decrypt_blk(void *handle, unsigned char *blk,
-				      int len)
+static void blowfish_ssh2_decrypt_blk(void *handle, void *blk, int len)
 {
     BlowfishContext *ctx = (BlowfishContext *)handle;
     blowfish_msb_decrypt_cbc(blk, len, ctx);
 }
 
-static void blowfish_ssh2_sdctr(void *handle, unsigned char *blk,
-				      int len)
+static void blowfish_ssh2_sdctr(void *handle, void *blk, int len)
 {
     BlowfishContext *ctx = (BlowfishContext *)handle;
     blowfish_msb_sdctr(blk, len, ctx);

+ 3 - 4
source/putty/sshblowf.h

@@ -9,7 +9,6 @@ void *blowfish_make_context(void);
 void blowfish_free_context(void *handle);
 void blowfish_initkey(BlowfishContext *ctx);
 void blowfish_expandkey(BlowfishContext *ctx,
-                        const unsigned char *key, short keybytes,
-                        const unsigned char *salt, short saltbytes);
-void blowfish_lsb_encrypt_ecb(unsigned char *blk, int len,
-                              BlowfishContext *ctx);
+                        const void *key, short keybytes,
+                        const void *salt, short saltbytes);
+void blowfish_lsb_encrypt_ecb(void *blk, int len, BlowfishContext *ctx);

+ 56 - 61
source/putty/sshbn.c

@@ -17,6 +17,7 @@
 typedef BignumInt *Bignum;
 
 #include "ssh.h"
+#include "marshal.h"
 
 BignumInt bnZero[1] = { 0 };
 BignumInt bnOne[2] = { 1, 1 };
@@ -1403,8 +1404,9 @@ void decbn(Bignum bn)
     bn[i]--;
 }
 
-Bignum bignum_from_bytes(const unsigned char *data, int nbytes)
+Bignum bignum_from_bytes(const void *vdata, int nbytes)
 {
+    const unsigned char *data = (const unsigned char *)vdata;
     Bignum result;
     int w, i;
 
@@ -1425,8 +1427,9 @@ Bignum bignum_from_bytes(const unsigned char *data, int nbytes)
     return result;
 }
 
-Bignum bignum_from_bytes_le(const unsigned char *data, int nbytes)
+Bignum bignum_from_bytes_le(const void *vdata, int nbytes)
 {
+    const unsigned char *data = (const unsigned char *)vdata;
     Bignum result;
     int w, i;
 
@@ -1506,36 +1509,7 @@ Bignum bignum_random_in_range(const Bignum lower, const Bignum upper)
 }
 
 /*
- * Read an SSH-1-format bignum from a data buffer. Return the number
- * of bytes consumed, or -1 if there wasn't enough data.
- */
-int ssh1_read_bignum(const unsigned char *data, int len, Bignum * result)
-{
-    const unsigned char *p = data;
-    int i;
-    int w, b;
-
-    if (len < 2)
-	return -1;
-
-    w = 0;
-    for (i = 0; i < 2; i++)
-	w = (w << 8) + *p++;
-    b = (w + 7) / 8;		       /* bits -> bytes */
-
-    if (len < b+2)
-	return -1;
-
-    if (!result)		       /* just return length */
-	return b + 2;
-
-    *result = bignum_from_bytes(p, b);
-
-    return p + b - data;
-}
-
-/*
- * Return the bit count of a bignum, for SSH-1 encoding.
+ * Return the bit count of a bignum.
  */
 int bignum_bitcount(Bignum bn)
 {
@@ -1545,22 +1519,6 @@ int bignum_bitcount(Bignum bn)
     return bitcount + 1;
 }
 
-/*
- * Return the byte length of a bignum when SSH-1 encoded.
- */
-int ssh1_bignum_length(Bignum bn)
-{
-    return 2 + (bignum_bitcount(bn) + 7) / 8;
-}
-
-/*
- * Return the byte length of a bignum when SSH-2 encoded.
- */
-int ssh2_bignum_length(Bignum bn)
-{
-    return 4 + (bignum_bitcount(bn) + 8) / 8;
-}
-
 /*
  * Return a byte from a bignum; 0 is least significant, etc.
  */
@@ -1601,22 +1559,59 @@ void bignum_set_bit(Bignum bn, int bitnum, int value)
     }
 }
 
-/*
- * Write a SSH-1-format bignum into a buffer. It is assumed the
- * buffer is big enough. Returns the number of bytes used.
- */
-int ssh1_write_bignum(void *data, Bignum bn)
+void BinarySink_put_mp_ssh1(BinarySink *bs, Bignum bn)
+{
+    int bits = bignum_bitcount(bn);
+    int bytes = (bits + 7) / 8;
+    int i;
+
+    put_uint16(bs, bits);
+    for (i = bytes; i--;)
+        put_byte(bs, bignum_byte(bn, i));
+}
+
+void BinarySink_put_mp_ssh2(BinarySink *bs, Bignum bn)
 {
-    unsigned char *p = data;
-    int len = ssh1_bignum_length(bn);
+    int bytes = (bignum_bitcount(bn) + 8) / 8;
     int i;
-    int bitc = bignum_bitcount(bn);
 
-    *p++ = (bitc >> 8) & 0xFF;
-    *p++ = (bitc) & 0xFF;
-    for (i = len - 2; i--;)
-	*p++ = bignum_byte(bn, i);
-    return len;
+    put_uint32(bs, bytes);
+    for (i = bytes; i--;)
+        put_byte(bs, bignum_byte(bn, i));
+}
+
+Bignum BinarySource_get_mp_ssh1(BinarySource *src)
+{
+    unsigned bitc = get_uint16(src);
+    ptrlen bytes = get_data(src, (bitc + 7) / 8);
+    if (get_err(src)) {
+        return bignum_from_long(0);
+    } else {
+        Bignum toret = bignum_from_bytes(bytes.ptr, bytes.len);
+        if (bignum_bitcount(toret) != bitc) {
+            src->err = BSE_INVALID;
+            freebn(toret);
+            toret = bignum_from_long(0);
+        }
+        return toret;
+    }
+}
+
+Bignum BinarySource_get_mp_ssh2(BinarySource *src)
+{
+    ptrlen bytes = get_string(src);
+    if (get_err(src)) {
+        return bignum_from_long(0);
+    } else {
+        const unsigned char *p = bytes.ptr;
+        if ((bytes.len > 0 &&
+             ((p[0] & 0x80) ||
+              (p[0] == 0 && (bytes.len <= 1 || !(p[1] & 0x80)))))) {
+            src->err = BSE_INVALID;
+            return bignum_from_long(0);
+        }
+        return bignum_from_bytes(bytes.ptr, bytes.len);
+    }
 }
 
 /*

+ 62 - 0
source/putty/sshbpp.h

@@ -0,0 +1,62 @@
+/*
+ * Abstraction of the binary packet protocols used in SSH.
+ */
+
+#ifndef PUTTY_SSHBPP_H
+#define PUTTY_SSHBPP_H
+
+typedef struct BinaryPacketProtocol BinaryPacketProtocol;
+
+struct BinaryPacketProtocolVtable {
+    void (*free)(BinaryPacketProtocol *); 
+    void (*handle_input)(BinaryPacketProtocol *);
+    PktOut *(*new_pktout)(int type);
+    void (*format_packet)(BinaryPacketProtocol *, PktOut *);
+};
+
+struct BinaryPacketProtocol {
+    const struct BinaryPacketProtocolVtable *vt;
+    bufchain *in_raw, *out_raw;
+    PacketQueue *in_pq;
+    PacketLogSettings *pls;
+    void *logctx;
+
+    int seen_disconnect;
+    char *error;
+};
+
+#define ssh_bpp_free(bpp) ((bpp)->vt->free(bpp))
+#define ssh_bpp_handle_input(bpp) ((bpp)->vt->handle_input(bpp))
+#define ssh_bpp_new_pktout(bpp, type) ((bpp)->vt->new_pktout(type))
+#define ssh_bpp_format_packet(bpp, pkt) ((bpp)->vt->format_packet(bpp, pkt))
+
+BinaryPacketProtocol *ssh1_bpp_new(void);
+void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
+                         const struct ssh_cipher *cipher,
+                         const void *session_key);
+void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp);
+
+BinaryPacketProtocol *ssh2_bpp_new(void);
+void ssh2_bpp_new_outgoing_crypto(
+    BinaryPacketProtocol *bpp,
+    const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
+    const struct ssh_mac *mac, int etm_mode, const void *mac_key,
+    const struct ssh_compress *compression);
+void ssh2_bpp_new_incoming_crypto(
+    BinaryPacketProtocol *bpp,
+    const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
+    const struct ssh_mac *mac, int etm_mode, const void *mac_key,
+    const struct ssh_compress *compression);
+
+BinaryPacketProtocol *ssh2_bare_bpp_new(void);
+
+#ifdef MPEXT
+const struct ssh_cipher * ssh1_bpp_get_cipher(BinaryPacketProtocol *bpp);
+int ssh1_bpp_get_compressing(BinaryPacketProtocol *bpp);
+const struct ssh2_cipher * ssh2_bpp_get_cscipher(BinaryPacketProtocol *bpp);
+const struct ssh2_cipher * ssh2_bpp_get_sccipher(BinaryPacketProtocol *bpp);
+const struct ssh_compress * ssh2_bpp_get_cscomp(BinaryPacketProtocol *bpp);
+const struct ssh_compress * ssh2_bpp_get_sccomp(BinaryPacketProtocol *bpp);
+#endif
+
+#endif /* PUTTY_SSHBPP_H */

+ 35 - 24
source/putty/sshccp.c

@@ -864,6 +864,8 @@ struct ccp_context {
     unsigned char mac_iv[8];
 
     struct poly1305 mac;
+
+    BinarySink_IMPLEMENTATION;
 };
 
 static void *poly_make_context(void *ctx)
@@ -876,7 +878,7 @@ static void poly_free_context(void *ctx)
     /* Not allocated, just forwarded, no need to free */
 }
 
-static void poly_setkey(void *ctx, unsigned char *key)
+static void poly_setkey(void *ctx, const void *key)
 {
     /* Uses the same context as ChaCha20, so ignore */
 }
@@ -890,9 +892,10 @@ static void poly_start(void *handle)
     poly1305_init(&ctx->mac);
 }
 
-static void poly_bytes(void *handle, unsigned char const *blk, int len)
+static void poly_BinarySink_write(BinarySink *bs, const void *blkv, size_t len)
 {
-    struct ccp_context *ctx = (struct ccp_context *)handle;
+    struct ccp_context *ctx = BinarySink_DOWNCAST(bs, struct ccp_context);
+    const unsigned char *blk = (const unsigned char *)blkv;
 
     /* First 4 bytes are the IV */
     while (ctx->mac_initialised < 4 && len) {
@@ -922,6 +925,12 @@ static void poly_bytes(void *handle, unsigned char const *blk, int len)
     }
 }
 
+static BinarySink *poly_sink(void *handle)
+{
+    struct ccp_context *ctx = (struct ccp_context *)handle;
+    return BinarySink_UPCAST(ctx);
+}
+
 static void poly_genresult(void *handle, unsigned char *blk)
 {
     struct ccp_context *ctx = (struct ccp_context *)handle;
@@ -939,25 +948,27 @@ static int poly_verresult(void *handle, unsigned char const *blk)
 }
 
 /* The generic poly operation used before generate and verify */
-static void poly_op(void *handle, unsigned char *blk, int len, unsigned long seq)
+static void poly_op(void *handle, const unsigned char *blk, int len,
+                    unsigned long seq)
 {
-    unsigned char iv[4];
-    poly_start(handle);
-    PUT_32BIT_MSB_FIRST(iv, seq);
-    /* poly_bytes expects the first 4 bytes to be the IV */
-    poly_bytes(handle, iv, 4);
-    smemclr(iv, sizeof(iv));
-    poly_bytes(handle, blk, len);
+    struct ccp_context *ctx = (struct ccp_context *)handle;
+    poly_start(ctx);
+    /* the data receiver expects the first 4 bytes to be the IV */
+    put_uint32(ctx, seq);
+    put_data(ctx, blk, len);
 }
 
-static void poly_generate(void *handle, unsigned char *blk, int len, unsigned long seq)
+static void poly_generate(void *handle, void *vblk, int len, unsigned long seq)
 {
+    unsigned char *blk = (unsigned char *)vblk;
     poly_op(handle, blk, len, seq);
     poly_genresult(handle, blk+len);
 }
 
-static int poly_verify(void *handle, unsigned char *blk, int len, unsigned long seq)
+static int poly_verify(void *handle, const void *vblk, int len,
+                       unsigned long seq)
 {
+    const unsigned char *blk = (const unsigned char *)vblk;
     poly_op(handle, blk, len, seq);
     return poly_verresult(handle, blk+len);
 }
@@ -970,7 +981,7 @@ static const struct ssh_mac ssh2_poly1305 = {
     poly_generate, poly_verify,
 
     /* partial-packet operations */
-    poly_start, poly_bytes, poly_genresult, poly_verresult,
+    poly_start, poly_sink, poly_genresult, poly_verresult,
 
     "", "", /* Not selectable individually, just part of ChaCha20-Poly1305 */
     16, 0, "Poly1305"
@@ -979,9 +990,8 @@ static const struct ssh_mac ssh2_poly1305 = {
 static void *ccp_make_context(void)
 {
     struct ccp_context *ctx = snew(struct ccp_context);
-    if (ctx) {
-        poly1305_init(&ctx->mac);
-    }
+    BinarySink_INIT(ctx, poly_BinarySink_write);
+    poly1305_init(&ctx->mac);
     return ctx;
 }
 
@@ -994,14 +1004,15 @@ static void ccp_free_context(void *vctx)
     sfree(ctx);
 }
 
-static void ccp_iv(void *vctx, unsigned char *iv)
+static void ccp_iv(void *vctx, const void *iv)
 {
     /* struct ccp_context *ctx = (struct ccp_context *)vctx; */
     /* IV is set based on the sequence number */
 }
 
-static void ccp_key(void *vctx, unsigned char *key)
+static void ccp_key(void *vctx, const void *vkey)
 {
+    const unsigned char *key = (const unsigned char *)vkey;
     struct ccp_context *ctx = (struct ccp_context *)vctx;
     /* Initialise the a_cipher (for decrypting lengths) with the first 256 bits */
     chacha20_key(&ctx->a_cipher, key + 32);
@@ -1009,19 +1020,19 @@ static void ccp_key(void *vctx, unsigned char *key)
     chacha20_key(&ctx->b_cipher, key);
 }
 
-static void ccp_encrypt(void *vctx, unsigned char *blk, int len)
+static void ccp_encrypt(void *vctx, void *blk, int len)
 {
     struct ccp_context *ctx = (struct ccp_context *)vctx;
     chacha20_encrypt(&ctx->b_cipher, blk, len);
 }
 
-static void ccp_decrypt(void *vctx, unsigned char *blk, int len)
+static void ccp_decrypt(void *vctx, void *blk, int len)
 {
     struct ccp_context *ctx = (struct ccp_context *)vctx;
     chacha20_decrypt(&ctx->b_cipher, blk, len);
 }
 
-static void ccp_length_op(struct ccp_context *ctx, unsigned char *blk, int len,
+static void ccp_length_op(struct ccp_context *ctx, void *blk, int len,
                           unsigned long seq)
 {
     unsigned char iv[8];
@@ -1038,7 +1049,7 @@ static void ccp_length_op(struct ccp_context *ctx, unsigned char *blk, int len,
     smemclr(iv, sizeof(iv));
 }
 
-static void ccp_encrypt_length(void *vctx, unsigned char *blk, int len,
+static void ccp_encrypt_length(void *vctx, void *blk, int len,
                                unsigned long seq)
 {
     struct ccp_context *ctx = (struct ccp_context *)vctx;
@@ -1046,7 +1057,7 @@ static void ccp_encrypt_length(void *vctx, unsigned char *blk, int len,
     chacha20_encrypt(&ctx->a_cipher, blk, len);
 }
 
-static void ccp_decrypt_length(void *vctx, unsigned char *blk, int len,
+static void ccp_decrypt_length(void *vctx, void *blk, int len,
                                unsigned long seq)
 {
     struct ccp_context *ctx = (struct ccp_context *)vctx;

+ 54 - 0
source/putty/sshcr.h

@@ -0,0 +1,54 @@
+/*
+ * Coroutine mechanics used in PuTTY's SSH code.
+ */
+
+#ifndef PUTTY_SSHCR_H
+#define PUTTY_SSHCR_H
+
+/*
+ * If these macros look impenetrable to you, you might find it helpful
+ * to read
+ * 
+ *   https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
+ * 
+ * which explains the theory behind these macros.
+ * 
+ * In particular, if you are getting `case expression not constant'
+ * errors when building with MS Visual Studio, this is because MS's
+ * Edit and Continue debugging feature causes their compiler to
+ * violate ANSI C. To disable Edit and Continue debugging:
+ * 
+ *  - right-click ssh.c in the FileView
+ *  - click Settings
+ *  - select the C/C++ tab and the General category
+ *  - under `Debug info:', select anything _other_ than `Program
+ *    Database for Edit and Continue'.
+ */
+
+#define crBegin(v)      { int *crLine = &v; switch(v) { case 0:;
+#define crBeginState    crBegin(s->crLine)
+#define crStateP(t, v)                          \
+    struct t *s;                                \
+    if (!(v)) { s = (v) = snew(struct t); s->crLine = 0; }      \
+    s = (v);
+#define crState(t)      crStateP(t, ssh->t)
+#define crFinish(z)     } *crLine = 0; return (z); }
+#define crFinishV       } *crLine = 0; return; }
+#define crFinishFree(z) } sfree(s); return (z); }
+#define crFinishFreeV   } sfree(s); return; }
+#define crReturn(z)     \
+        do {\
+            *crLine =__LINE__; return (z); case __LINE__:;\
+        } while (0)
+#define crReturnV       \
+        do {\
+            *crLine=__LINE__; return; case __LINE__:;\
+        } while (0)
+#define crStop(z)       do{ *crLine = 0; return (z); }while(0)
+#define crStopV         do{ *crLine = 0; return; }while(0)
+#define crWaitUntil(c)  do { crReturn(0); } while (!(c))
+#define crWaitUntilV(c) do { crReturnV; } while (!(c))
+#define crMaybeWaitUntil(c) do { while (!(c)) crReturn(0); } while (0)
+#define crMaybeWaitUntilV(c) do { while (!(c)) crReturnV; } while (0)
+
+#endif /* PUTTY_SSHCR_H */

+ 39 - 27
source/putty/sshdes.c

@@ -774,8 +774,9 @@ static void des3_free_context(void *handle)   /* used for both 3DES and DES */
     sfree(handle);
 }
 
-static void des3_key(void *handle, unsigned char *key)
+static void des3_key(void *handle, const void *vkey)
 {
+    const unsigned char *key = (const unsigned char *)vkey;
     DESContext *keys = (DESContext *) handle;
     des_key_setup(GET_32BIT_MSB_FIRST(key),
 		  GET_32BIT_MSB_FIRST(key + 4), &keys[0]);
@@ -785,71 +786,75 @@ static void des3_key(void *handle, unsigned char *key)
 		  GET_32BIT_MSB_FIRST(key + 20), &keys[2]);
 }
 
-static void des3_iv(void *handle, unsigned char *key)
+static void des3_iv(void *handle, const void *viv)
 {
+    const unsigned char *iv = (const unsigned char *)viv;
     DESContext *keys = (DESContext *) handle;
-    keys[0].iv0 = GET_32BIT_MSB_FIRST(key);
-    keys[0].iv1 = GET_32BIT_MSB_FIRST(key + 4);
+    keys[0].iv0 = GET_32BIT_MSB_FIRST(iv);
+    keys[0].iv1 = GET_32BIT_MSB_FIRST(iv + 4);
 }
 
-static void des_key(void *handle, unsigned char *key)
+static void des_key(void *handle, const void *vkey)
 {
+    const unsigned char *key = (const unsigned char *)vkey;
     DESContext *keys = (DESContext *) handle;
     des_key_setup(GET_32BIT_MSB_FIRST(key),
 		  GET_32BIT_MSB_FIRST(key + 4), &keys[0]);
 }
 
-static void des3_sesskey(void *handle, unsigned char *key)
+static void des3_sesskey(void *handle, const void *key)
 {
     DESContext *keys = (DESContext *) handle;
     des3_key(keys, key);
     des3_key(keys+3, key);
 }
 
-static void des3_encrypt_blk(void *handle, unsigned char *blk, int len)
+static void des3_encrypt_blk(void *handle, void *blk, int len)
 {
     DESContext *keys = (DESContext *) handle;
     des_3cbc_encrypt(blk, len, keys);
 }
 
-static void des3_decrypt_blk(void *handle, unsigned char *blk, int len)
+static void des3_decrypt_blk(void *handle, void *blk, int len)
 {
     DESContext *keys = (DESContext *) handle;
     des_3cbc_decrypt(blk, len, keys+3);
 }
 
-static void des3_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len)
+static void des3_ssh2_encrypt_blk(void *handle, void *blk, int len)
 {
     DESContext *keys = (DESContext *) handle;
     des_cbc3_encrypt(blk, len, keys);
 }
 
-static void des3_ssh2_decrypt_blk(void *handle, unsigned char *blk, int len)
+static void des3_ssh2_decrypt_blk(void *handle, void *blk, int len)
 {
     DESContext *keys = (DESContext *) handle;
     des_cbc3_decrypt(blk, len, keys);
 }
 
-static void des3_ssh2_sdctr(void *handle, unsigned char *blk, int len)
+static void des3_ssh2_sdctr(void *handle, void *blk, int len)
 {
     DESContext *keys = (DESContext *) handle;
     des_sdctr3(blk, len, keys);
 }
 
-static void des_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len)
+static void des_ssh2_encrypt_blk(void *handle, void *blk, int len)
 {
     DESContext *keys = (DESContext *) handle;
     des_cbc_encrypt(blk, len, keys);
 }
 
-static void des_ssh2_decrypt_blk(void *handle, unsigned char *blk, int len)
+static void des_ssh2_decrypt_blk(void *handle, void *blk, int len)
 {
     DESContext *keys = (DESContext *) handle;
     des_cbc_decrypt(blk, len, keys);
 }
 
-void des3_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len)
+void des3_decrypt_pubkey(const void *vkey, void *vblk, int len)
 {
+    const unsigned char *key = (const unsigned char *)vkey;
+    unsigned char *blk = (unsigned char *)vblk;
     DESContext ourkeys[3];
     des_key_setup(GET_32BIT_MSB_FIRST(key),
 		  GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]);
@@ -861,8 +866,10 @@ void des3_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len)
     smemclr(ourkeys, sizeof(ourkeys));
 }
 
-void des3_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len)
+void des3_encrypt_pubkey(const void *vkey, void *vblk, int len)
 {
+    const unsigned char *key = (const unsigned char *)vkey;
+    unsigned char *blk = (unsigned char *)vblk;
     DESContext ourkeys[3];
     des_key_setup(GET_32BIT_MSB_FIRST(key),
 		  GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]);
@@ -874,9 +881,12 @@ void des3_encrypt_pubkey(unsigned char *key, unsigned char *blk, int len)
     smemclr(ourkeys, sizeof(ourkeys));
 }
 
-void des3_decrypt_pubkey_ossh(unsigned char *key, unsigned char *iv,
-			      unsigned char *blk, int len)
+void des3_decrypt_pubkey_ossh(const void *vkey, const void *viv,
+			      void *vblk, int len)
 {
+    const unsigned char *key = (const unsigned char *)vkey;
+    const unsigned char *iv = (const unsigned char *)viv;
+    unsigned char *blk = (unsigned char *)vblk;
     DESContext ourkeys[3];
     des_key_setup(GET_32BIT_MSB_FIRST(key),
 		  GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]);
@@ -890,9 +900,12 @@ void des3_decrypt_pubkey_ossh(unsigned char *key, unsigned char *iv,
     smemclr(ourkeys, sizeof(ourkeys));
 }
 
-void des3_encrypt_pubkey_ossh(unsigned char *key, unsigned char *iv,
-			      unsigned char *blk, int len)
+void des3_encrypt_pubkey_ossh(const void *vkey, const void *viv,
+			      void *vblk, int len)
 {
+    const unsigned char *key = (const unsigned char *)vkey;
+    const unsigned char *iv = (const unsigned char *)viv;
+    unsigned char *blk = (unsigned char *)vblk;
     DESContext ourkeys[3];
     des_key_setup(GET_32BIT_MSB_FIRST(key),
 		  GET_32BIT_MSB_FIRST(key + 4), &ourkeys[0]);
@@ -906,8 +919,9 @@ void des3_encrypt_pubkey_ossh(unsigned char *key, unsigned char *iv,
     smemclr(ourkeys, sizeof(ourkeys));
 }
 
-static void des_keysetup_xdmauth(const unsigned char *keydata, DESContext *dc)
+static void des_keysetup_xdmauth(const void *vkeydata, DESContext *dc)
 {
+    const unsigned char *keydata = (const unsigned char *)vkeydata;
     unsigned char key[8];
     int i, nbits, j;
     unsigned int bits;
@@ -929,16 +943,14 @@ static void des_keysetup_xdmauth(const unsigned char *keydata, DESContext *dc)
     des_key_setup(GET_32BIT_MSB_FIRST(key), GET_32BIT_MSB_FIRST(key + 4), dc);
 }
 
-void des_encrypt_xdmauth(const unsigned char *keydata,
-                         unsigned char *blk, int len)
+void des_encrypt_xdmauth(const void *keydata, void *blk, int len)
 {
     DESContext dc;
     des_keysetup_xdmauth(keydata, &dc);
     des_cbc_encrypt(blk, len, &dc);
 }
 
-void des_decrypt_xdmauth(const unsigned char *keydata,
-                         unsigned char *blk, int len)
+void des_decrypt_xdmauth(const void *keydata, void *blk, int len)
 {
     DESContext dc;
     des_keysetup_xdmauth(keydata, &dc);
@@ -1011,20 +1023,20 @@ const struct ssh_cipher ssh_3des = {
     8, "triple-DES inner-CBC"
 };
 
-static void des_sesskey(void *handle, unsigned char *key)
+static void des_sesskey(void *handle, const void *key)
 {
     DESContext *keys = (DESContext *) handle;
     des_key(keys, key);
     des_key(keys+1, key);
 }
 
-static void des_encrypt_blk(void *handle, unsigned char *blk, int len)
+static void des_encrypt_blk(void *handle, void *blk, int len)
 {
     DESContext *keys = (DESContext *) handle;
     des_cbc_encrypt(blk, len, keys);
 }
 
-static void des_decrypt_blk(void *handle, unsigned char *blk, int len)
+static void des_decrypt_blk(void *handle, void *blk, int len)
 {
     DESContext *keys = (DESContext *) handle;
     des_cbc_decrypt(blk, len, keys+1);

+ 50 - 5
source/putty/sshdh.c

@@ -77,12 +77,18 @@ static const struct dh_extra extra_group14 = {
     P14, G, lenof(P14), lenof(G),
 };
 
+static const struct ssh_kex ssh_diffiehellman_group14_sha256 = {
+    "diffie-hellman-group14-sha256", "group14",
+    KEXTYPE_DH, &ssh_sha256, &extra_group14,
+};
+
 static const struct ssh_kex ssh_diffiehellman_group14_sha1 = {
     "diffie-hellman-group14-sha1", "group14",
     KEXTYPE_DH, &ssh_sha1, &extra_group14,
 };
 
 static const struct ssh_kex *const group14_list[] = {
+    &ssh_diffiehellman_group14_sha256,
     &ssh_diffiehellman_group14_sha1
 };
 
@@ -115,6 +121,46 @@ const struct ssh_kexes ssh_diffiehellman_gex = {
     gex_list
 };
 
+/*
+ * Suffix on GSSAPI SSH protocol identifiers that indicates Kerberos 5
+ * as the mechanism.
+ *
+ * This suffix is the base64-encoded MD5 hash of the byte sequence
+ * 06 09 2A 86 48 86 F7 12 01 02 02, which in turn is the ASN.1 DER
+ * encoding of the object ID 1.2.840.113554.1.2.2 which designates
+ * Kerberos v5.
+ *
+ * (The same encoded OID, minus the two-byte DER header, is defined in
+ * pgssapi.c as GSS_MECH_KRB5.)
+ */
+#define GSS_KRB5_OID_HASH "toWM5Slw5Ew8Mqkay+al2g=="
+
+static const struct ssh_kex ssh_gssk5_diffiehellman_gex_sha1 = {
+    "gss-gex-sha1-" GSS_KRB5_OID_HASH, NULL,
+    KEXTYPE_GSS, &ssh_sha1, &extra_gex,
+};
+
+static const struct ssh_kex ssh_gssk5_diffiehellman_group14_sha1 = {
+    "gss-group14-sha1-" GSS_KRB5_OID_HASH, "group14",
+    KEXTYPE_GSS, &ssh_sha1, &extra_group14,
+};
+
+static const struct ssh_kex ssh_gssk5_diffiehellman_group1_sha1 = {
+    "gss-group1-sha1-" GSS_KRB5_OID_HASH, "group1",
+    KEXTYPE_GSS, &ssh_sha1, &extra_group1,
+};
+
+static const struct ssh_kex *const gssk5_sha1_kex_list[] = {
+    &ssh_gssk5_diffiehellman_gex_sha1,
+    &ssh_gssk5_diffiehellman_group14_sha1,
+    &ssh_gssk5_diffiehellman_group1_sha1
+};
+
+const struct ssh_kexes ssh_gssk5_sha1_kex = {
+    sizeof(gssk5_sha1_kex_list) / sizeof(*gssk5_sha1_kex_list),
+    gssk5_sha1_kex_list
+};
+
 /*
  * Variables.
  */
@@ -201,7 +247,7 @@ Bignum dh_create_e(void *handle, int nbits)
     int nbytes;
     unsigned char *buf;
 
-    nbytes = ssh1_bignum_length(ctx->qmask);
+    nbytes = (bignum_bitcount(ctx->qmask) + 7) / 8;
     buf = snewn(nbytes, unsigned char);
 
     do {
@@ -212,10 +258,9 @@ Bignum dh_create_e(void *handle, int nbits)
 	if (ctx->x)
 	    freebn(ctx->x);
 	if (nbits == 0 || nbits > bignum_bitcount(ctx->qmask)) {
-	    ssh1_write_bignum(buf, ctx->qmask);
-	    for (i = 2; i < nbytes; i++)
-		buf[i] &= random_byte();
-	    ssh1_read_bignum(buf, nbytes, &ctx->x);   /* can't fail */
+	    for (i = 0; i < nbytes; i++)
+		buf[i] = bignum_byte(ctx->qmask, i) & random_byte();
+	    ctx->x = bignum_from_bytes(buf, nbytes);
 	} else {
 	    int b, nb;
 	    ctx->x = bn_power_2(nbits);

+ 120 - 293
source/putty/sshdss.c

@@ -9,125 +9,38 @@
 #include "ssh.h"
 #include "misc.h"
 
-static void sha_mpint(SHA_State * s, Bignum b)
-{
-    unsigned char lenbuf[4];
-    int len;
-    len = (bignum_bitcount(b) + 8) / 8;
-    PUT_32BIT(lenbuf, len);
-    SHA_Bytes(s, lenbuf, 4);
-    while (len-- > 0) {
-	lenbuf[0] = bignum_byte(b, len);
-	SHA_Bytes(s, lenbuf, 1);
-    }
-    smemclr(lenbuf, sizeof(lenbuf));
-}
-
-static void sha512_mpint(SHA512_State * s, Bignum b)
-{
-    unsigned char lenbuf[4];
-    int len;
-    len = (bignum_bitcount(b) + 8) / 8;
-    PUT_32BIT(lenbuf, len);
-    SHA512_Bytes(s, lenbuf, 4);
-    while (len-- > 0) {
-	lenbuf[0] = bignum_byte(b, len);
-	SHA512_Bytes(s, lenbuf, 1);
-    }
-    smemclr(lenbuf, sizeof(lenbuf));
-}
+static void dss_freekey(ssh_key *key);    /* forward reference */
 
-static void getstring(const char **data, int *datalen,
-                      const char **p, int *length)
+static ssh_key *dss_new_pub(const ssh_keyalg *self, ptrlen data)
 {
-    *p = NULL;
-    if (*datalen < 4)
-	return;
-    *length = toint(GET_32BIT(*data));
-    if (*length < 0)
-        return;
-    *datalen -= 4;
-    *data += 4;
-    if (*datalen < *length)
-	return;
-    *p = *data;
-    *data += *length;
-    *datalen -= *length;
-}
-static Bignum getmp(const char **data, int *datalen)
-{
-    const char *p;
-    int length;
-    Bignum b;
+    BinarySource src[1];
+    struct dss_key *dss;
 
-    getstring(data, datalen, &p, &length);
-    if (!p)
+    BinarySource_BARE_INIT(src, data.ptr, data.len);
+    if (!ptrlen_eq_string(get_string(src), "ssh-dss"))
 	return NULL;
-    if (p[0] & 0x80)
-	return NULL;		       /* negative mp */
-    b = bignum_from_bytes((const unsigned char *)p, length);
-    return b;
-}
-
-static Bignum get160(const char **data, int *datalen)
-{
-    Bignum b;
-
-    if (*datalen < 20)
-        return NULL;
-
-    b = bignum_from_bytes((const unsigned char *)*data, 20);
-    *data += 20;
-    *datalen -= 20;
-
-    return b;
-}
-
-static void dss_freekey(void *key);    /* forward reference */
-
-static void *dss_newkey(const struct ssh_signkey *self,
-                        const char *data, int len)
-{
-    const char *p;
-    int slen;
-    struct dss_key *dss;
 
     dss = snew(struct dss_key);
-    getstring(&data, &len, &p, &slen);
-
-#ifdef DEBUG_DSS
-    {
-	int i;
-	printf("key:");
-	for (i = 0; i < len; i++)
-	    printf("  %02x", (unsigned char) (data[i]));
-	printf("\n");
-    }
-#endif
-
-    if (!p || slen != 7 || memcmp(p, "ssh-dss", 7)) {
-	sfree(dss);
-	return NULL;
-    }
-    dss->p = getmp(&data, &len);
-    dss->q = getmp(&data, &len);
-    dss->g = getmp(&data, &len);
-    dss->y = getmp(&data, &len);
+    dss->sshk = &ssh_dss;
+    dss->p = get_mp_ssh2(src);
+    dss->q = get_mp_ssh2(src);
+    dss->g = get_mp_ssh2(src);
+    dss->y = get_mp_ssh2(src);
     dss->x = NULL;
 
-    if (!dss->p || !dss->q || !dss->g || !dss->y ||
+    if (get_err(src) ||
         !bignum_cmp(dss->q, Zero) || !bignum_cmp(dss->p, Zero)) {
         /* Invalid key. */
-        dss_freekey(dss);
+        dss_freekey(&dss->sshk);
         return NULL;
     }
 
-    return dss;
+    return &dss->sshk;
 }
 
-static void dss_freekey(void *key)
+static void dss_freekey(ssh_key *key)
 {
-    struct dss_key *dss = (struct dss_key *) key;
+    struct dss_key *dss = FROMFIELD(key, struct dss_key, sshk);
     if (dss->p)
         freebn(dss->p);
     if (dss->q)
@@ -141,9 +54,9 @@ static void dss_freekey(void *key)
     sfree(dss);
 }
 
-static char *dss_fmtkey(void *key)
+static char *dss_cache_str(ssh_key *key)
 {
-    struct dss_key *dss = (struct dss_key *) key;
+    struct dss_key *dss = FROMFIELD(key, struct dss_key, sshk);
     char *p;
     int len, i, pos, nibbles;
     static const char hex[] = "0123456789abcdef";
@@ -191,28 +104,19 @@ static char *dss_fmtkey(void *key)
     return p;
 }
 
-static int dss_verifysig(void *key, const char *sig, int siglen,
-			 const char *data, int datalen)
+static int dss_verify(ssh_key *key, ptrlen sig, ptrlen data)
 {
-    struct dss_key *dss = (struct dss_key *) key;
-    const char *p;
-    int slen;
-    char hash[20];
+    struct dss_key *dss = FROMFIELD(key, struct dss_key, sshk);
+    BinarySource src[1];
+    unsigned char hash[20];
     Bignum r, s, w, gu1p, yu2p, gu1yu2p, u1, u2, sha, v;
     int ret;
 
     if (!dss->p)
 	return 0;
 
-#ifdef DEBUG_DSS
-    {
-	int i;
-	printf("sig:");
-	for (i = 0; i < siglen; i++)
-	    printf("  %02x", (unsigned char) (sig[i]));
-	printf("\n");
-    }
-#endif
+    BinarySource_BARE_INIT(src, sig.ptr, sig.len);
+
     /*
      * Commercial SSH (2.0.13) and OpenSSH disagree over the format
      * of a DSA signature. OpenSSH is in line with RFC 4253:
@@ -224,15 +128,18 @@ static int dss_verifysig(void *key, const char *sig, int siglen,
      * the length: length 40 means the commercial-SSH bug, anything
      * else is assumed to be RFC-compliant.
      */
-    if (siglen != 40) {		       /* bug not present; read admin fields */
-	getstring(&sig, &siglen, &p, &slen);
-	if (!p || slen != 7 || memcmp(p, "ssh-dss", 7)) {
-	    return 0;
-	}
-	sig += 4, siglen -= 4;	       /* skip yet another length field */
+    if (sig.len != 40) {      /* bug not present; read admin fields */
+	ptrlen type = get_string(src);
+        sig = get_string(src);
+
+        if (get_err(src) || !ptrlen_eq_string(type, "ssh-dss") ||
+            sig.len != 40)
+            return 0;
     }
-    r = get160(&sig, &siglen);
-    s = get160(&sig, &siglen);
+
+    /* Now we're sitting on a 40-byte string for sure. */
+    r = bignum_from_bytes(sig.ptr, 20);
+    s = bignum_from_bytes((const char *)sig.ptr + 20, 20);
     if (!r || !s) {
         if (r)
             freebn(r);
@@ -260,10 +167,8 @@ static int dss_verifysig(void *key, const char *sig, int siglen,
     /*
      * Step 2. u1 <- SHA(message) * w mod q.
      */
-    SHA_Simple(data, datalen, (unsigned char *)hash);
-    p = hash;
-    slen = 20;
-    sha = get160(&p, &slen);
+    SHA_Simple(data.ptr, data.len, hash);
+    sha = bignum_from_bytes(hash, 20);
     u1 = modmul(sha, w, dss->q);
 
     /*
@@ -299,108 +204,58 @@ static int dss_verifysig(void *key, const char *sig, int siglen,
     return ret;
 }
 
-static unsigned char *dss_public_blob(void *key, int *len)
+static void dss_public_blob(ssh_key *key, BinarySink *bs)
 {
-    struct dss_key *dss = (struct dss_key *) key;
-    int plen, qlen, glen, ylen, bloblen;
-    int i;
-    unsigned char *blob, *p;
-
-    plen = (bignum_bitcount(dss->p) + 8) / 8;
-    qlen = (bignum_bitcount(dss->q) + 8) / 8;
-    glen = (bignum_bitcount(dss->g) + 8) / 8;
-    ylen = (bignum_bitcount(dss->y) + 8) / 8;
+    struct dss_key *dss = FROMFIELD(key, struct dss_key, sshk);
 
-    /*
-     * string "ssh-dss", mpint p, mpint q, mpint g, mpint y. Total
-     * 27 + sum of lengths. (five length fields, 20+7=27).
-     */
-    bloblen = 27 + plen + qlen + glen + ylen;
-    blob = snewn(bloblen, unsigned char);
-    p = blob;
-    PUT_32BIT(p, 7);
-    p += 4;
-    memcpy(p, "ssh-dss", 7);
-    p += 7;
-    PUT_32BIT(p, plen);
-    p += 4;
-    for (i = plen; i--;)
-	*p++ = bignum_byte(dss->p, i);
-    PUT_32BIT(p, qlen);
-    p += 4;
-    for (i = qlen; i--;)
-	*p++ = bignum_byte(dss->q, i);
-    PUT_32BIT(p, glen);
-    p += 4;
-    for (i = glen; i--;)
-	*p++ = bignum_byte(dss->g, i);
-    PUT_32BIT(p, ylen);
-    p += 4;
-    for (i = ylen; i--;)
-	*p++ = bignum_byte(dss->y, i);
-    assert(p == blob + bloblen);
-    *len = bloblen;
-    return blob;
+    put_stringz(bs, "ssh-dss");
+    put_mp_ssh2(bs, dss->p);
+    put_mp_ssh2(bs, dss->q);
+    put_mp_ssh2(bs, dss->g);
+    put_mp_ssh2(bs, dss->y);
 }
 
-static unsigned char *dss_private_blob(void *key, int *len)
+static void dss_private_blob(ssh_key *key, BinarySink *bs)
 {
-    struct dss_key *dss = (struct dss_key *) key;
-    int xlen, bloblen;
-    int i;
-    unsigned char *blob, *p;
+    struct dss_key *dss = FROMFIELD(key, struct dss_key, sshk);
 
-    xlen = (bignum_bitcount(dss->x) + 8) / 8;
-
-    /*
-     * mpint x, string[20] the SHA of p||q||g. Total 4 + xlen.
-     */
-    bloblen = 4 + xlen;
-    blob = snewn(bloblen, unsigned char);
-    p = blob;
-    PUT_32BIT(p, xlen);
-    p += 4;
-    for (i = xlen; i--;)
-	*p++ = bignum_byte(dss->x, i);
-    assert(p == blob + bloblen);
-    *len = bloblen;
-    return blob;
+    put_mp_ssh2(bs, dss->x);
 }
 
-static void *dss_createkey(const struct ssh_signkey *self,
-                           const unsigned char *pub_blob, int pub_len,
-			   const unsigned char *priv_blob, int priv_len)
+static ssh_key *dss_new_priv(const ssh_keyalg *self, ptrlen pub, ptrlen priv)
 {
+    BinarySource src[1];
+    ssh_key *sshk;
     struct dss_key *dss;
-    const char *pb = (const char *) priv_blob;
-    const char *hash;
-    int hashlen;
+    ptrlen hash;
     SHA_State s;
     unsigned char digest[20];
     Bignum ytest;
 
-    dss = dss_newkey(self, (char *) pub_blob, pub_len);
-    if (!dss)
+    sshk = dss_new_pub(self, pub);
+    if (!sshk)
         return NULL;
-    dss->x = getmp(&pb, &priv_len);
-    if (!dss->x) {
-        dss_freekey(dss);
+
+    dss = FROMFIELD(sshk, struct dss_key, sshk);
+    BinarySource_BARE_INIT(src, priv.ptr, priv.len);
+    dss->x = get_mp_ssh2(src);
+    if (get_err(src)) {
+        dss_freekey(&dss->sshk);
         return NULL;
     }
 
     /*
      * Check the obsolete hash in the old DSS key format.
      */
-    hashlen = -1;
-    getstring(&pb, &priv_len, &hash, &hashlen);
-    if (hashlen == 20) {
+    hash = get_string(src);
+    if (hash.len == 20) {
 	SHA_Init(&s);
-	sha_mpint(&s, dss->p);
-	sha_mpint(&s, dss->q);
-	sha_mpint(&s, dss->g);
+	put_mp_ssh2(&s, dss->p);
+	put_mp_ssh2(&s, dss->q);
+	put_mp_ssh2(&s, dss->g);
 	SHA_Final(&s, digest);
-	if (0 != memcmp(hash, digest, 20)) {
-	    dss_freekey(dss);
+	if (0 != memcmp(hash.ptr, digest, 20)) {
+	    dss_freekey(&dss->sshk);
 	    return NULL;
 	}
     }
@@ -410,78 +265,63 @@ static void *dss_createkey(const struct ssh_signkey *self,
      */
     ytest = modpow(dss->g, dss->x, dss->p);
     if (0 != bignum_cmp(ytest, dss->y)) {
-	dss_freekey(dss);
+	dss_freekey(&dss->sshk);
         freebn(ytest);
 	return NULL;
     }
     freebn(ytest);
 
-    return dss;
+    return &dss->sshk;
 }
 
-static void *dss_openssh_createkey(const struct ssh_signkey *self,
-                                   const unsigned char **blob, int *len)
+static ssh_key *dss_new_priv_openssh(const ssh_keyalg *self,
+                                     BinarySource *src)
 {
-    const char **b = (const char **) blob;
     struct dss_key *dss;
 
     dss = snew(struct dss_key);
+    dss->sshk = &ssh_dss;
 
-    dss->p = getmp(b, len);
-    dss->q = getmp(b, len);
-    dss->g = getmp(b, len);
-    dss->y = getmp(b, len);
-    dss->x = getmp(b, len);
+    dss->p = get_mp_ssh2(src);
+    dss->q = get_mp_ssh2(src);
+    dss->g = get_mp_ssh2(src);
+    dss->y = get_mp_ssh2(src);
+    dss->x = get_mp_ssh2(src);
 
-    if (!dss->p || !dss->q || !dss->g || !dss->y || !dss->x ||
+    if (get_err(src) ||
         !bignum_cmp(dss->q, Zero) || !bignum_cmp(dss->p, Zero)) {
         /* Invalid key. */
-        dss_freekey(dss);
+        dss_freekey(&dss->sshk);
         return NULL;
     }
 
-    return dss;
+    return &dss->sshk;
 }
 
-static int dss_openssh_fmtkey(void *key, unsigned char *blob, int len)
+static void dss_openssh_blob(ssh_key *key, BinarySink *bs)
 {
-    struct dss_key *dss = (struct dss_key *) key;
-    int bloblen, i;
-
-    bloblen =
-	ssh2_bignum_length(dss->p) +
-	ssh2_bignum_length(dss->q) +
-	ssh2_bignum_length(dss->g) +
-	ssh2_bignum_length(dss->y) +
-	ssh2_bignum_length(dss->x);
-
-    if (bloblen > len)
-	return bloblen;
-
-    bloblen = 0;
-#define ENC(x) \
-    PUT_32BIT(blob+bloblen, ssh2_bignum_length((x))-4); bloblen += 4; \
-    for (i = ssh2_bignum_length((x))-4; i-- ;) blob[bloblen++]=bignum_byte((x),i);
-    ENC(dss->p);
-    ENC(dss->q);
-    ENC(dss->g);
-    ENC(dss->y);
-    ENC(dss->x);
-
-    return bloblen;
+    struct dss_key *dss = FROMFIELD(key, struct dss_key, sshk);
+
+    put_mp_ssh2(bs, dss->p);
+    put_mp_ssh2(bs, dss->q);
+    put_mp_ssh2(bs, dss->g);
+    put_mp_ssh2(bs, dss->y);
+    put_mp_ssh2(bs, dss->x);
 }
 
-static int dss_pubkey_bits(const struct ssh_signkey *self,
-                           const void *blob, int len)
+static int dss_pubkey_bits(const ssh_keyalg *self, ptrlen pub)
 {
+    ssh_key *sshk;
     struct dss_key *dss;
     int ret;
 
-    dss = dss_newkey(self, (const char *) blob, len);
-    if (!dss)
+    sshk = dss_new_pub(self, pub);
+    if (!sshk)
         return -1;
+
+    dss = FROMFIELD(sshk, struct dss_key, sshk);
     ret = bignum_bitcount(dss->p);
-    dss_freekey(dss);
+    dss_freekey(&dss->sshk);
 
     return ret;
 }
@@ -568,16 +408,16 @@ Bignum *dss_gen_k(const char *id_string, Bignum modulus, Bignum private_key,
      * Hash some identifying text plus x.
      */
     SHA512_Init(&ss);
-    SHA512_Bytes(&ss, id_string, strlen(id_string) + 1);
-    sha512_mpint(&ss, private_key);
+    put_asciz(&ss, id_string);
+    put_mp_ssh2(&ss, private_key);
     SHA512_Final(&ss, digest512);
 
     /*
      * Now hash that digest plus the message hash.
      */
     SHA512_Init(&ss);
-    SHA512_Bytes(&ss, digest512, sizeof(digest512));
-    SHA512_Bytes(&ss, digest, digest_len);
+    put_data(&ss, digest512, sizeof(digest512));
+    put_data(&ss, digest, digest_len);
 
     while (1) {
         SHA512_State ss2 = ss;         /* structure copy */
@@ -601,19 +441,18 @@ Bignum *dss_gen_k(const char *id_string, Bignum modulus, Bignum private_key,
         /* Very unlikely we get here, but if so, k was unsuitable. */
         freebn(k);
         /* Perturb the hash to think of a different k. */
-        SHA512_Bytes(&ss, "x", 1);
+        put_byte(&ss, 'x');
         /* Go round and try again. */
     }
 }
 
-static unsigned char *dss_sign(void *key, const char *data, int datalen,
-                               int *siglen)
+static void dss_sign(ssh_key *key, const void *data, int datalen,
+                     BinarySink *bs)
 {
-    struct dss_key *dss = (struct dss_key *) key;
+    struct dss_key *dss = FROMFIELD(key, struct dss_key, sshk);
     Bignum k, gkp, hash, kinv, hxr, r, s;
     unsigned char digest[20];
-    unsigned char *bytes;
-    int nbytes, i;
+    int i;
 
     SHA_Simple(data, datalen, digest);
 
@@ -637,43 +476,31 @@ static unsigned char *dss_sign(void *key, const char *data, int datalen,
     freebn(k);
     freebn(hash);
 
-    /*
-     * Signature blob is
-     * 
-     *   string  "ssh-dss"
-     *   string  two 20-byte numbers r and s, end to end
-     * 
-     * i.e. 4+7 + 4+40 bytes.
-     */
-    nbytes = 4 + 7 + 4 + 40;
-    bytes = snewn(nbytes, unsigned char);
-    PUT_32BIT(bytes, 7);
-    memcpy(bytes + 4, "ssh-dss", 7);
-    PUT_32BIT(bytes + 4 + 7, 40);
-    for (i = 0; i < 20; i++) {
-	bytes[4 + 7 + 4 + i] = bignum_byte(r, 19 - i);
-	bytes[4 + 7 + 4 + 20 + i] = bignum_byte(s, 19 - i);
-    }
+    put_stringz(bs, "ssh-dss");
+    put_uint32(bs, 40);
+    for (i = 0; i < 20; i++)
+	put_byte(bs, bignum_byte(r, 19 - i));
+    for (i = 0; i < 20; i++)
+        put_byte(bs, bignum_byte(s, 19 - i));
     freebn(r);
     freebn(s);
-
-    *siglen = nbytes;
-    return bytes;
 }
 
-const struct ssh_signkey ssh_dss = {
-    dss_newkey,
+const ssh_keyalg ssh_dss = {
+    dss_new_pub,
+    dss_new_priv,
+    dss_new_priv_openssh,
+
     dss_freekey,
-    dss_fmtkey,
+    dss_sign,
+    dss_verify,
     dss_public_blob,
     dss_private_blob,
-    dss_createkey,
-    dss_openssh_createkey,
-    dss_openssh_fmtkey,
-    5 /* p,q,g,y,x */,
+    dss_openssh_blob,
+    dss_cache_str,
+
     dss_pubkey_bits,
-    dss_verifysig,
-    dss_sign,
+
     "ssh-dss",
     "dss",
     NULL,

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 215 - 401
source/putty/sshecc.c


+ 20 - 3
source/putty/sshgss.h

@@ -13,6 +13,8 @@ typedef enum Ssh_gss_stat {
     SSH_GSS_S_CONTINUE_NEEDED,
     SSH_GSS_NO_MEM,
     SSH_GSS_BAD_HOST_NAME,
+    SSH_GSS_BAD_MIC,
+    SSH_GSS_NO_CREDS,
     SSH_GSS_FAILURE
 } Ssh_gss_stat;
 
@@ -26,6 +28,10 @@ typedef enum Ssh_gss_stat {
 typedef gss_buffer_desc Ssh_gss_buf;
 typedef gss_name_t Ssh_gss_name;
 
+#define GSS_NO_EXPIRATION ((time_t)-1)
+
+#define GSS_DEF_REKEY_MINS 2	/* Default minutes between GSS cache checks */
+
 /* Functions, provided by either wingss.c or sshgssc.c */
 
 struct ssh_gss_library;
@@ -79,7 +85,8 @@ typedef Ssh_gss_stat (*t_ssh_gss_release_name)(struct ssh_gss_library *lib,
 typedef Ssh_gss_stat (*t_ssh_gss_init_sec_context)
     (struct ssh_gss_library *lib,
      Ssh_gss_ctx *ctx, Ssh_gss_name name, int delegate,
-     Ssh_gss_buf *in, Ssh_gss_buf *out);
+     Ssh_gss_buf *in, Ssh_gss_buf *out, time_t *expiry,
+     unsigned long *lifetime);
 
 /*
  * Frees the contents of an Ssh_gss_buf filled in by
@@ -96,7 +103,8 @@ typedef Ssh_gss_stat (*t_ssh_gss_free_tok)(struct ssh_gss_library *lib,
  * place. Needs to be freed by ssh_gss_release_cred().
  */
 typedef Ssh_gss_stat (*t_ssh_gss_acquire_cred)(struct ssh_gss_library *lib,
-					       Ssh_gss_ctx *);
+                                               Ssh_gss_ctx *,
+                                               time_t *expiry);
 
 /*
  * Frees the contents of an Ssh_gss_ctx filled in by
@@ -111,7 +119,15 @@ typedef Ssh_gss_stat (*t_ssh_gss_release_cred)(struct ssh_gss_library *lib,
  */
 typedef Ssh_gss_stat (*t_ssh_gss_get_mic)(struct ssh_gss_library *lib,
 					  Ssh_gss_ctx ctx, Ssh_gss_buf *in,
-                 Ssh_gss_buf *out);
+                                          Ssh_gss_buf *out);
+
+/*
+ * Validates an input MIC for some input data.
+ */
+typedef Ssh_gss_stat (*t_ssh_gss_verify_mic)(struct ssh_gss_library *lib,
+                                             Ssh_gss_ctx ctx,
+                                             Ssh_gss_buf *in_data,
+                                             Ssh_gss_buf *in_mic);
 
 /*
  * Frees the contents of an Ssh_gss_buf filled in by
@@ -161,6 +177,7 @@ struct ssh_gss_library {
     t_ssh_gss_acquire_cred acquire_cred;
     t_ssh_gss_release_cred release_cred;
     t_ssh_gss_get_mic get_mic;
+    t_ssh_gss_verify_mic verify_mic;
     t_ssh_gss_free_mic free_mic;
     t_ssh_gss_display_status display_status;
 

+ 93 - 5
source/putty/sshgssc.c

@@ -1,6 +1,7 @@
 #include "putty.h"
 
 #include <string.h>
+#include <limits.h>
 #include "sshgssc.h"
 #include "misc.h"
 
@@ -38,14 +39,73 @@ static Ssh_gss_stat ssh_gssapi_import_name(struct ssh_gss_library *lib,
 }
 
 static Ssh_gss_stat ssh_gssapi_acquire_cred(struct ssh_gss_library *lib,
-					    Ssh_gss_ctx *ctx)
+                                            Ssh_gss_ctx *ctx,
+                                            time_t *expiry)
 {
+    struct gssapi_functions *gss = &lib->u.gssapi;
+    #ifdef MPEXT
+    gss_OID_set_desc k5only;
+    #else
+    gss_OID_set_desc k5only = { 1, GSS_MECH_KRB5 };
+    #endif
+    gss_cred_id_t cred;
+    OM_uint32 dummy;
+    OM_uint32 time_rec;
     gssapi_ssh_gss_ctx *gssctx = snew(gssapi_ssh_gss_ctx);
 
-    gssctx->maj_stat =  gssctx->min_stat = GSS_S_COMPLETE;
+    #ifdef MPEXT
+    k5only.count = 1;
+    k5only.elements = GSS_MECH_KRB5;
+    #endif
+
     gssctx->ctx = GSS_C_NO_CONTEXT;
-    *ctx = (Ssh_gss_ctx) gssctx;
+    gssctx->expiry = 0;
+
+    gssctx->maj_stat =
+        gss->acquire_cred(&gssctx->min_stat, GSS_C_NO_NAME, GSS_C_INDEFINITE,
+                          &k5only, GSS_C_INITIATE, &cred,
+                          (gss_OID_set *)0, &time_rec);
+
+    if (gssctx->maj_stat != GSS_S_COMPLETE) {
+        sfree(gssctx);
+        return SSH_GSS_FAILURE;
+    }
+
+    /*
+     * When the credential lifetime is not yet available due to deferred
+     * processing, gss_acquire_cred should return a 0 lifetime which is
+     * distinct from GSS_C_INDEFINITE which signals a crential that never
+     * expires.  However, not all implementations get this right, and with
+     * Kerberos, initiator credentials always expire at some point.  So when
+     * lifetime is 0 or GSS_C_INDEFINITE we call gss_inquire_cred_by_mech() to
+     * complete deferred processing.
+     */
+    if (time_rec == GSS_C_INDEFINITE || time_rec == 0) {
+        gssctx->maj_stat =
+            gss->inquire_cred_by_mech(&gssctx->min_stat, cred,
+                                      (gss_OID) GSS_MECH_KRB5,
+                                      GSS_C_NO_NAME,
+                                      &time_rec,
+                                      NULL,
+                                      NULL);
+    }
+    (void) gss->release_cred(&dummy, &cred);
+
+    if (gssctx->maj_stat != GSS_S_COMPLETE) {
+        sfree(gssctx);
+        return SSH_GSS_FAILURE;
+    }
 
+    if (time_rec != GSS_C_INDEFINITE)
+        gssctx->expiry = time(NULL) + time_rec;
+    else
+        gssctx->expiry = GSS_NO_EXPIRATION;
+
+    if (expiry) {
+        *expiry = gssctx->expiry;
+    }
+
+    *ctx = (Ssh_gss_ctx) gssctx;
     return SSH_GSS_OK;
 }
 
@@ -54,11 +114,14 @@ static Ssh_gss_stat ssh_gssapi_init_sec_context(struct ssh_gss_library *lib,
 						Ssh_gss_name srv_name,
 						int to_deleg,
 						Ssh_gss_buf *recv_tok,
-						Ssh_gss_buf *send_tok)
+                                                Ssh_gss_buf *send_tok,
+                                                time_t *expiry,
+                                                unsigned long *lifetime)
 {
     struct gssapi_functions *gss = &lib->u.gssapi;
     gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx*) *ctx;
     OM_uint32 ret_flags;
+    OM_uint32 lifetime_rec;
 
     if (to_deleg) to_deleg = GSS_C_DELEG_FLAG;
     gssctx->maj_stat = gss->init_sec_context(&gssctx->min_stat,
@@ -74,7 +137,20 @@ static Ssh_gss_stat ssh_gssapi_init_sec_context(struct ssh_gss_library *lib,
 					     NULL,   /* ignore mech type */
 					     send_tok,
 					     &ret_flags,
-					     NULL);  /* ignore time_rec */
+                                             &lifetime_rec);
+
+    if (lifetime) {
+        if (lifetime_rec == GSS_C_INDEFINITE)
+            *lifetime = ULONG_MAX;
+        else
+            *lifetime = lifetime_rec;
+    }
+    if (expiry) {
+        if (lifetime_rec == GSS_C_INDEFINITE)
+            *expiry = GSS_NO_EXPIRATION;
+        else
+            *expiry = time(NULL) + lifetime_rec;
+    }
 
     if (gssctx->maj_stat == GSS_S_COMPLETE) return SSH_GSS_S_COMPLETE;
     if (gssctx->maj_stat == GSS_S_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED;
@@ -148,6 +224,7 @@ static Ssh_gss_stat ssh_gssapi_release_cred(struct ssh_gss_library *lib,
     if (gssctx->ctx != GSS_C_NO_CONTEXT)
         maj_stat = gss->delete_sec_context(&min_stat,&gssctx->ctx,GSS_C_NO_BUFFER);
     sfree(gssctx);
+    *ctx = NULL;
 
     if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
     return SSH_GSS_FAILURE;
@@ -175,6 +252,16 @@ static Ssh_gss_stat ssh_gssapi_get_mic(struct ssh_gss_library *lib,
     return gss->get_mic(&(gssctx->min_stat), gssctx->ctx, 0, buf, hash);
 }
 
+static Ssh_gss_stat ssh_gssapi_verify_mic(struct ssh_gss_library *lib,
+                                          Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
+                                          Ssh_gss_buf *hash)
+{
+    struct gssapi_functions *gss = &lib->u.gssapi;
+    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) ctx;
+    if (gssctx == NULL) return SSH_GSS_FAILURE;
+    return gss->verify_mic(&(gssctx->min_stat), gssctx->ctx, buf, hash, NULL);
+}
+
 static Ssh_gss_stat ssh_gssapi_free_mic(struct ssh_gss_library *lib,
 					Ssh_gss_buf *hash)
 {
@@ -192,6 +279,7 @@ void ssh_gssapi_bind_fns(struct ssh_gss_library *lib)
     lib->acquire_cred = ssh_gssapi_acquire_cred;
     lib->release_cred = ssh_gssapi_release_cred;
     lib->get_mic = ssh_gssapi_get_mic;
+    lib->verify_mic = ssh_gssapi_verify_mic;
     lib->free_mic = ssh_gssapi_free_mic;
     lib->display_status = ssh_gssapi_display_status;
 }

+ 1 - 0
source/putty/sshgssc.h

@@ -10,6 +10,7 @@ typedef struct gssapi_ssh_gss_ctx {
     OM_uint32 maj_stat;
     OM_uint32 min_stat;
     gss_ctx_id_t ctx;
+    time_t expiry;
 } gssapi_ssh_gss_ctx;
 
 void ssh_gssapi_bind_fns(struct ssh_gss_library *lib);

+ 29 - 16
source/putty/sshmd5.c

@@ -1,3 +1,4 @@
+#include <assert.h>
 #include "ssh.h"
 
 /*
@@ -115,20 +116,26 @@ static void MD5_Block(MD5_Core_State * s, uint32 * block)
 
 #define BLKSIZE 64
 
+static void MD5_BinarySink_write(BinarySink *bs, const void *data, size_t len);
+
 void MD5Init(struct MD5Context *s)
 {
     MD5_Core_Init(&s->core);
     s->blkused = 0;
     s->lenhi = s->lenlo = 0;
+    BinarySink_INIT(s, MD5_BinarySink_write);
 }
 
-void MD5Update(struct MD5Context *s, unsigned char const *p, unsigned len)
+static void MD5_BinarySink_write(BinarySink *bs, const void *data, size_t len)
 {
-    unsigned char *q = (unsigned char *) p;
+    struct MD5Context *s = BinarySink_DOWNCAST(bs, struct MD5Context);
+    const unsigned char *q = (const unsigned char *)data;
     uint32 wordblock[16];
     uint32 lenw = len;
     int i;
 
+    assert(lenw == len);
+
     /*
      * Update the length field.
      */
@@ -185,7 +192,7 @@ void MD5Final(unsigned char output[16], struct MD5Context *s)
 
     memset(c, 0, pad);
     c[0] = 0x80;
-    MD5Update(s, c, pad);
+    put_data(s, c, pad);
 
     c[7] = (lenhi >> 24) & 0xFF;
     c[6] = (lenhi >> 16) & 0xFF;
@@ -196,7 +203,7 @@ void MD5Final(unsigned char output[16], struct MD5Context *s)
     c[1] = (lenlo >> 8) & 0xFF;
     c[0] = (lenlo >> 0) & 0xFF;
 
-    MD5Update(s, c, 8);
+    put_data(s, c, 8);
 
     for (i = 0; i < 4; i++) {
 	output[4 * i + 3] = (s->core.h[i] >> 24) & 0xFF;
@@ -211,7 +218,7 @@ void MD5Simple(void const *p, unsigned len, unsigned char output[16])
     struct MD5Context s;
 
     MD5Init(&s);
-    MD5Update(&s, (unsigned char const *)p, len);
+    put_data(&s, (unsigned char const *)p, len);
     MD5Final(output, &s);
     smemclr(&s, sizeof(s));
 }
@@ -246,18 +253,18 @@ void hmacmd5_key(void *handle, void const *keyv, int len)
     for (i = 0; i < len && i < 64; i++)
 	foo[i] ^= key[i];
     MD5Init(&keys[0]);
-    MD5Update(&keys[0], foo, 64);
+    put_data(&keys[0], foo, 64);
 
     memset(foo, 0x5C, 64);
     for (i = 0; i < len && i < 64; i++)
 	foo[i] ^= key[i];
     MD5Init(&keys[1]);
-    MD5Update(&keys[1], foo, 64);
+    put_data(&keys[1], foo, 64);
 
     smemclr(foo, 64);		       /* burn the evidence */
 }
 
-static void hmacmd5_key_16(void *handle, unsigned char *key)
+static void hmacmd5_key_16(void *handle, const void *key)
 {
     hmacmd5_key(handle, key, 16);
 }
@@ -267,12 +274,13 @@ static void hmacmd5_start(void *handle)
     struct MD5Context *keys = (struct MD5Context *)handle;
 
     keys[2] = keys[0];		      /* structure copy */
+    BinarySink_COPIED(&keys[2]);
 }
 
-static void hmacmd5_bytes(void *handle, unsigned char const *blk, int len)
+static BinarySink *hmacmd5_sink(void *handle)
 {
     struct MD5Context *keys = (struct MD5Context *)handle;
-    MD5Update(&keys[2], blk, len);
+    return BinarySink_UPCAST(&keys[2]);
 }
 
 static void hmacmd5_genresult(void *handle, unsigned char *hmac)
@@ -282,9 +290,11 @@ static void hmacmd5_genresult(void *handle, unsigned char *hmac)
     unsigned char intermediate[16];
 
     s = keys[2];		       /* structure copy */
+    BinarySink_COPIED(&s);
     MD5Final(intermediate, &s);
     s = keys[1];		       /* structure copy */
-    MD5Update(&s, intermediate, 16);
+    BinarySink_COPIED(&s);
+    put_data(&s, intermediate, 16);
     MD5Final(hmac, &s);
 }
 
@@ -300,9 +310,10 @@ static void hmacmd5_do_hmac_internal(void *handle,
 				     unsigned char const *blk2, int len2,
 				     unsigned char *hmac)
 {
+    BinarySink *bs = hmacmd5_sink(handle);
     hmacmd5_start(handle);
-    hmacmd5_bytes(handle, blk, len);
-    if (blk2) hmacmd5_bytes(handle, blk2, len2);
+    put_data(bs, blk, len);
+    if (blk2) put_data(bs, blk2, len2);
     hmacmd5_genresult(handle, hmac);
 }
 
@@ -321,15 +332,17 @@ static void hmacmd5_do_hmac_ssh(void *handle, unsigned char const *blk, int len,
     hmacmd5_do_hmac_internal(handle, seqbuf, 4, blk, len, hmac);
 }
 
-static void hmacmd5_generate(void *handle, unsigned char *blk, int len,
+static void hmacmd5_generate(void *handle, void *vblk, int len,
 			     unsigned long seq)
 {
+    unsigned char *blk = (unsigned char *)vblk;
     hmacmd5_do_hmac_ssh(handle, blk, len, seq, blk + len);
 }
 
-static int hmacmd5_verify(void *handle, unsigned char *blk, int len,
+static int hmacmd5_verify(void *handle, const void *vblk, int len,
 			  unsigned long seq)
 {
+    const unsigned char *blk = (const unsigned char *)vblk;
     unsigned char correct[16];
     hmacmd5_do_hmac_ssh(handle, blk, len, seq, correct);
     return smemeq(correct, blk + len, 16);
@@ -338,7 +351,7 @@ static int hmacmd5_verify(void *handle, unsigned char *blk, int len,
 const struct ssh_mac ssh_hmac_md5 = {
     hmacmd5_make_context, hmacmd5_free_context, hmacmd5_key_16,
     hmacmd5_generate, hmacmd5_verify,
-    hmacmd5_start, hmacmd5_bytes, hmacmd5_genresult, hmacmd5_verresult,
+    hmacmd5_start, hmacmd5_sink, hmacmd5_genresult, hmacmd5_verresult,
     "hmac-md5", "[email protected]",
     16, 16,
     "HMAC-MD5"

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 234 - 328
source/putty/sshpubk.c


+ 194 - 355
source/putty/sshrsa.c

@@ -10,61 +10,39 @@
 #include "ssh.h"
 #include "misc.h"
 
-int makekey(const unsigned char *data, int len, struct RSAKey *result,
-	    const unsigned char **keystr, int order)
-{
-    const unsigned char *p = data;
-    int i, n;
-
-    if (len < 4)
-	return -1;
-
-    if (result) {
-	result->bits = 0;
-	for (i = 0; i < 4; i++)
-	    result->bits = (result->bits << 8) + *p++;
-    } else
-	p += 4;
-
-    len -= 4;
-
-    /*
-     * order=0 means exponent then modulus (the keys sent by the
-     * server). order=1 means modulus then exponent (the keys
-     * stored in a keyfile).
-     */
-
-    if (order == 0) {
-	n = ssh1_read_bignum(p, len, result ? &result->exponent : NULL);
-	if (n < 0) return -1;
-	p += n;
-	len -= n;
+void BinarySource_get_rsa_ssh1_pub(
+    BinarySource *src, struct RSAKey *rsa, RsaSsh1Order order)
+{
+    unsigned bits;
+    Bignum e, m;
+
+    bits = get_uint32(src);
+    if (order == RSA_SSH1_EXPONENT_FIRST) {
+        e = get_mp_ssh1(src);
+        m = get_mp_ssh1(src);
+    } else {
+        m = get_mp_ssh1(src);
+        e = get_mp_ssh1(src);
     }
 
-    n = ssh1_read_bignum(p, len, result ? &result->modulus : NULL);
-    if (n < 0 || (result && bignum_bitcount(result->modulus) == 0)) return -1;
-    if (result)
-	result->bytes = n - 2;
-    if (keystr)
-	*keystr = p + 2;
-    p += n;
-    len -= n;
-
-    if (order == 1) {
-	n = ssh1_read_bignum(p, len, result ? &result->exponent : NULL);
-	if (n < 0) return -1;
-	p += n;
-	len -= n;
+    if (rsa) {
+        rsa->bits = bits;
+        rsa->exponent = e;
+        rsa->modulus = m;
+        rsa->bytes = (bignum_bitcount(m) + 7) / 8;
+    } else {
+        freebn(e);
+        freebn(m);
     }
-    return p - data;
 }
 
-int makeprivate(const unsigned char *data, int len, struct RSAKey *result)
+void BinarySource_get_rsa_ssh1_priv(
+    BinarySource *src, struct RSAKey *rsa)
 {
-    return ssh1_read_bignum(data, len, &result->private_exponent);
+    rsa->private_exponent = get_mp_ssh1(src);
 }
 
-int rsaencrypt(unsigned char *data, int length, struct RSAKey *key)
+int rsa_ssh1_encrypt(unsigned char *data, int length, struct RSAKey *key)
 {
     Bignum b1, b2;
     int i;
@@ -99,20 +77,6 @@ int rsaencrypt(unsigned char *data, int length, struct RSAKey *key)
     return 1;
 }
 
-static void sha512_mpint(SHA512_State * s, Bignum b)
-{
-    unsigned char lenbuf[4];
-    int len;
-    len = (bignum_bitcount(b) + 8) / 8;
-    PUT_32BIT(lenbuf, len);
-    SHA512_Bytes(s, lenbuf, 4);
-    while (len-- > 0) {
-	lenbuf[0] = bignum_byte(b, len);
-	SHA512_Bytes(s, lenbuf, 1);
-    }
-    smemclr(lenbuf, sizeof(lenbuf));
-}
-
 /*
  * Compute (base ^ exp) % mod, provided mod == p * q, with p,q
  * distinct primes, and iqmp is the multiplicative inverse of q mod p.
@@ -237,12 +201,10 @@ static Bignum rsa_privkey_op(Bignum input, struct RSAKey *key)
 		 *    byte = random_byte();
 		 */
 		if (digestused >= lenof(digest512)) {
-		    unsigned char seqbuf[4];
-		    PUT_32BIT(seqbuf, hashseq);
 		    SHA512_Init(&ss);
-		    SHA512_Bytes(&ss, "RSA deterministic blinding", 26);
-		    SHA512_Bytes(&ss, seqbuf, sizeof(seqbuf));
-		    sha512_mpint(&ss, key->private_exponent);
+		    put_data(&ss, "RSA deterministic blinding", 26);
+		    put_uint32(&ss, hashseq);
+		    put_mp_ssh2(&ss, key->private_exponent);
 		    SHA512_Final(&ss, digest512);
 		    hashseq++;
 
@@ -251,8 +213,8 @@ static Bignum rsa_privkey_op(Bignum input, struct RSAKey *key)
 		     * input.
 		     */
 		    SHA512_Init(&ss);
-		    SHA512_Bytes(&ss, digest512, sizeof(digest512));
-		    sha512_mpint(&ss, input);
+		    put_data(&ss, digest512, sizeof(digest512));
+		    put_mp_ssh2(&ss, input);
 		    SHA512_Final(&ss, digest512);
 
 		    digestused = 0;
@@ -318,7 +280,7 @@ static Bignum rsa_privkey_op(Bignum input, struct RSAKey *key)
     return ret;
 }
 
-Bignum rsadecrypt(Bignum input, struct RSAKey *key)
+Bignum rsa_ssh1_decrypt(Bignum input, struct RSAKey *key)
 {
     return rsa_privkey_op(input, key);
 }
@@ -367,38 +329,25 @@ void rsastr_fmt(char *str, struct RSAKey *key)
  * Generate a fingerprint string for the key. Compatible with the
  * OpenSSH fingerprint code.
  */
-void rsa_fingerprint(char *str, int len, struct RSAKey *key)
+char *rsa_ssh1_fingerprint(struct RSAKey *key)
 {
     struct MD5Context md5c;
     unsigned char digest[16];
-    char buffer[16 * 3 + 40];
-    int numlen, slen, i;
+    strbuf *out;
+    int i;
 
     MD5Init(&md5c);
-    numlen = ssh1_bignum_length(key->modulus) - 2;
-    for (i = numlen; i--;) {
-	unsigned char c = bignum_byte(key->modulus, i);
-	MD5Update(&md5c, &c, 1);
-    }
-    numlen = ssh1_bignum_length(key->exponent) - 2;
-    for (i = numlen; i--;) {
-	unsigned char c = bignum_byte(key->exponent, i);
-	MD5Update(&md5c, &c, 1);
-    }
+    put_mp_ssh1(&md5c, key->modulus);
+    put_mp_ssh1(&md5c, key->exponent);
     MD5Final(digest, &md5c);
 
-    sprintf(buffer, "%d ", bignum_bitcount(key->modulus));
+    out = strbuf_new();
+    strbuf_catf(out, "%d ", bignum_bitcount(key->modulus));
     for (i = 0; i < 16; i++)
-	sprintf(buffer + strlen(buffer), "%s%02x", i ? ":" : "",
-		digest[i]);
-    strncpy(str, buffer, len);
-    str[len - 1] = '\0';
-    slen = strlen(str);
-    if (key->comment && slen < len - 1) {
-	str[slen] = ' ';
-	strncpy(str + slen + 1, key->comment, len - slen - 1);
-	str[len - 1] = '\0';
-    }
+	strbuf_catf(out, "%s%02x", i ? ":" : "", digest[i]);
+    if (key->comment)
+        strbuf_catf(out, " %s", key->comment);
+    return strbuf_to_str(out);
 }
 
 /*
@@ -468,47 +417,37 @@ int rsa_verify(struct RSAKey *key)
     return 1;
 }
 
-/* Public key blob as used by Pageant: exponent before modulus. */
-unsigned char *rsa_public_blob(struct RSAKey *key, int *len)
+void rsa_ssh1_public_blob(BinarySink *bs, struct RSAKey *key,
+                          RsaSsh1Order order)
 {
-    int length, pos;
-    unsigned char *ret;
-
-    length = (ssh1_bignum_length(key->modulus) +
-	      ssh1_bignum_length(key->exponent) + 4);
-    ret = snewn(length, unsigned char);
-
-    PUT_32BIT(ret, bignum_bitcount(key->modulus));
-    pos = 4;
-    pos += ssh1_write_bignum(ret + pos, key->exponent);
-    pos += ssh1_write_bignum(ret + pos, key->modulus);
-
-    *len = length;
-    return ret;
+    put_uint32(bs, bignum_bitcount(key->modulus));
+    if (order == RSA_SSH1_EXPONENT_FIRST) {
+        put_mp_ssh1(bs, key->exponent);
+        put_mp_ssh1(bs, key->modulus);
+    } else {
+        put_mp_ssh1(bs, key->modulus);
+        put_mp_ssh1(bs, key->exponent);
+    }
 }
 
-/* Given a public blob, determine its length. */
-int rsa_public_blob_len(void *data, int maxlen)
+/* Given an SSH-1 public key blob, determine its length. */
+int rsa_ssh1_public_blob_len(void *data, int maxlen)
 {
-    unsigned char *p = (unsigned char *)data;
-    int n;
+    BinarySource src[1];
 
-    if (maxlen < 4)
-	return -1;
-    p += 4;			       /* length word */
-    maxlen -= 4;
+    BinarySource_BARE_INIT(src, data, maxlen);
 
-    n = ssh1_read_bignum(p, maxlen, NULL);    /* exponent */
-    if (n < 0)
-	return -1;
-    p += n;
+    /* Expect a length word, then exponent and modulus. (It doesn't
+     * even matter which order.) */
+    get_uint32(src);
+    freebn(get_mp_ssh1(src));
+    freebn(get_mp_ssh1(src));
 
-    n = ssh1_read_bignum(p, maxlen, NULL);    /* modulus */
-    if (n < 0)
+    if (get_err(src))
 	return -1;
-    p += n;
 
-    return p - (unsigned char *)data;
+    /* Return the number of bytes consumed. */
+    return src->pos;
 }
 
 void freersakey(struct RSAKey *key)
@@ -533,76 +472,43 @@ void freersakey(struct RSAKey *key)
  * Implementation of the ssh-rsa signing key type. 
  */
 
-static void getstring(const char **data, int *datalen,
-                      const char **p, int *length)
-{
-    *p = NULL;
-    if (*datalen < 4)
-	return;
-    *length = toint(GET_32BIT(*data));
-    if (*length < 0)
-        return;
-    *datalen -= 4;
-    *data += 4;
-    if (*datalen < *length)
-	return;
-    *p = *data;
-    *data += *length;
-    *datalen -= *length;
-}
-static Bignum getmp(const char **data, int *datalen)
-{
-    const char *p;
-    int length;
-    Bignum b;
-
-    getstring(data, datalen, &p, &length);
-    if (!p)
-	return NULL;
-    b = bignum_from_bytes((unsigned char *)p, length);
-    return b;
-}
+static void rsa2_freekey(ssh_key *key);   /* forward reference */
 
-static void rsa2_freekey(void *key);   /* forward reference */
-
-static void *rsa2_newkey(const struct ssh_signkey *self,
-                         const char *data, int len)
+static ssh_key *rsa2_new_pub(const ssh_keyalg *self, ptrlen data)
 {
-    const char *p;
-    int slen;
+    BinarySource src[1];
     struct RSAKey *rsa;
 
-    rsa = snew(struct RSAKey);
-    getstring(&data, &len, &p, &slen);
-
-    if (!p || slen != 7 || memcmp(p, "ssh-rsa", 7)) {
-	sfree(rsa);
+    BinarySource_BARE_INIT(src, data.ptr, data.len);
+    if (!ptrlen_eq_string(get_string(src), "ssh-rsa"))
 	return NULL;
-    }
-    rsa->exponent = getmp(&data, &len);
-    rsa->modulus = getmp(&data, &len);
+
+    rsa = snew(struct RSAKey);
+    rsa->sshk = &ssh_rsa;
+    rsa->exponent = get_mp_ssh2(src);
+    rsa->modulus = get_mp_ssh2(src);
     rsa->private_exponent = NULL;
     rsa->p = rsa->q = rsa->iqmp = NULL;
     rsa->comment = NULL;
 
-    if (!rsa->exponent || !rsa->modulus) {
-        rsa2_freekey(rsa);
-        return NULL;
+    if (get_err(src)) {
+	rsa2_freekey(&rsa->sshk);
+	return NULL;
     }
 
-    return rsa;
+    return &rsa->sshk;
 }
 
-static void rsa2_freekey(void *key)
+static void rsa2_freekey(ssh_key *key)
 {
-    struct RSAKey *rsa = (struct RSAKey *) key;
+    struct RSAKey *rsa = FROMFIELD(key, struct RSAKey, sshk);
     freersakey(rsa);
     sfree(rsa);
 }
 
-static char *rsa2_fmtkey(void *key)
+static char *rsa2_cache_str(ssh_key *key)
 {
-    struct RSAKey *rsa = (struct RSAKey *) key;
+    struct RSAKey *rsa = FROMFIELD(key, struct RSAKey, sshk);
     char *p;
     int len;
 
@@ -612,171 +518,100 @@ static char *rsa2_fmtkey(void *key)
     return p;
 }
 
-static unsigned char *rsa2_public_blob(void *key, int *len)
+static void rsa2_public_blob(ssh_key *key, BinarySink *bs)
 {
-    struct RSAKey *rsa = (struct RSAKey *) key;
-    int elen, mlen, bloblen;
-    int i;
-    unsigned char *blob, *p;
+    struct RSAKey *rsa = FROMFIELD(key, struct RSAKey, sshk);
 
-    elen = (bignum_bitcount(rsa->exponent) + 8) / 8;
-    mlen = (bignum_bitcount(rsa->modulus) + 8) / 8;
-
-    /*
-     * string "ssh-rsa", mpint exp, mpint mod. Total 19+elen+mlen.
-     * (three length fields, 12+7=19).
-     */
-    bloblen = 19 + elen + mlen;
-    blob = snewn(bloblen, unsigned char);
-    p = blob;
-    PUT_32BIT(p, 7);
-    p += 4;
-    memcpy(p, "ssh-rsa", 7);
-    p += 7;
-    PUT_32BIT(p, elen);
-    p += 4;
-    for (i = elen; i--;)
-	*p++ = bignum_byte(rsa->exponent, i);
-    PUT_32BIT(p, mlen);
-    p += 4;
-    for (i = mlen; i--;)
-	*p++ = bignum_byte(rsa->modulus, i);
-    assert(p == blob + bloblen);
-    *len = bloblen;
-    return blob;
+    put_stringz(bs, "ssh-rsa");
+    put_mp_ssh2(bs, rsa->exponent);
+    put_mp_ssh2(bs, rsa->modulus);
 }
 
-static unsigned char *rsa2_private_blob(void *key, int *len)
+static void rsa2_private_blob(ssh_key *key, BinarySink *bs)
 {
-    struct RSAKey *rsa = (struct RSAKey *) key;
-    int dlen, plen, qlen, ulen, bloblen;
-    int i;
-    unsigned char *blob, *p;
+    struct RSAKey *rsa = FROMFIELD(key, struct RSAKey, sshk);
 
-    dlen = (bignum_bitcount(rsa->private_exponent) + 8) / 8;
-    plen = (bignum_bitcount(rsa->p) + 8) / 8;
-    qlen = (bignum_bitcount(rsa->q) + 8) / 8;
-    ulen = (bignum_bitcount(rsa->iqmp) + 8) / 8;
-
-    /*
-     * mpint private_exp, mpint p, mpint q, mpint iqmp. Total 16 +
-     * sum of lengths.
-     */
-    bloblen = 16 + dlen + plen + qlen + ulen;
-    blob = snewn(bloblen, unsigned char);
-    p = blob;
-    PUT_32BIT(p, dlen);
-    p += 4;
-    for (i = dlen; i--;)
-	*p++ = bignum_byte(rsa->private_exponent, i);
-    PUT_32BIT(p, plen);
-    p += 4;
-    for (i = plen; i--;)
-	*p++ = bignum_byte(rsa->p, i);
-    PUT_32BIT(p, qlen);
-    p += 4;
-    for (i = qlen; i--;)
-	*p++ = bignum_byte(rsa->q, i);
-    PUT_32BIT(p, ulen);
-    p += 4;
-    for (i = ulen; i--;)
-	*p++ = bignum_byte(rsa->iqmp, i);
-    assert(p == blob + bloblen);
-    *len = bloblen;
-    return blob;
+    put_mp_ssh2(bs, rsa->private_exponent);
+    put_mp_ssh2(bs, rsa->p);
+    put_mp_ssh2(bs, rsa->q);
+    put_mp_ssh2(bs, rsa->iqmp);
 }
 
-static void *rsa2_createkey(const struct ssh_signkey *self,
-                            const unsigned char *pub_blob, int pub_len,
-			    const unsigned char *priv_blob, int priv_len)
+static ssh_key *rsa2_new_priv(const ssh_keyalg *self,
+                               ptrlen pub, ptrlen priv)
 {
+    BinarySource src[1];
+    ssh_key *sshk;
     struct RSAKey *rsa;
-    const char *pb = (const char *) priv_blob;
 
-    rsa = rsa2_newkey(self, (char *) pub_blob, pub_len);
-    rsa->private_exponent = getmp(&pb, &priv_len);
-    rsa->p = getmp(&pb, &priv_len);
-    rsa->q = getmp(&pb, &priv_len);
-    rsa->iqmp = getmp(&pb, &priv_len);
+    sshk = rsa2_new_pub(self, pub);
+    if (!sshk)
+        return NULL;
+
+    rsa = FROMFIELD(sshk, struct RSAKey, sshk);
+    BinarySource_BARE_INIT(src, priv.ptr, priv.len);
+    rsa->private_exponent = get_mp_ssh2(src);
+    rsa->p = get_mp_ssh2(src);
+    rsa->q = get_mp_ssh2(src);
+    rsa->iqmp = get_mp_ssh2(src);
 
-    if (!rsa_verify(rsa)) {
-	rsa2_freekey(rsa);
+    if (get_err(src) || !rsa_verify(rsa)) {
+	rsa2_freekey(&rsa->sshk);
 	return NULL;
     }
 
-    return rsa;
+    return &rsa->sshk;
 }
 
-static void *rsa2_openssh_createkey(const struct ssh_signkey *self,
-                                    const unsigned char **blob, int *len)
+static ssh_key *rsa2_new_priv_openssh(const ssh_keyalg *self,
+                                      BinarySource *src)
 {
-    const char **b = (const char **) blob;
     struct RSAKey *rsa;
 
     rsa = snew(struct RSAKey);
+    rsa->sshk = &ssh_rsa;
     rsa->comment = NULL;
 
-    rsa->modulus = getmp(b, len);
-    rsa->exponent = getmp(b, len);
-    rsa->private_exponent = getmp(b, len);
-    rsa->iqmp = getmp(b, len);
-    rsa->p = getmp(b, len);
-    rsa->q = getmp(b, len);
+    rsa->modulus = get_mp_ssh2(src);
+    rsa->exponent = get_mp_ssh2(src);
+    rsa->private_exponent = get_mp_ssh2(src);
+    rsa->iqmp = get_mp_ssh2(src);
+    rsa->p = get_mp_ssh2(src);
+    rsa->q = get_mp_ssh2(src);
 
-    if (!rsa->modulus || !rsa->exponent || !rsa->private_exponent ||
-	!rsa->iqmp || !rsa->p || !rsa->q) {
-        rsa2_freekey(rsa);
+    if (get_err(src) || !rsa_verify(rsa)) {
+	rsa2_freekey(&rsa->sshk);
 	return NULL;
     }
 
-    if (!rsa_verify(rsa)) {
-	rsa2_freekey(rsa);
-	return NULL;
-    }
-
-    return rsa;
+    return &rsa->sshk;
 }
 
-static int rsa2_openssh_fmtkey(void *key, unsigned char *blob, int len)
+static void rsa2_openssh_blob(ssh_key *key, BinarySink *bs)
 {
-    struct RSAKey *rsa = (struct RSAKey *) key;
-    int bloblen, i;
-
-    bloblen =
-	ssh2_bignum_length(rsa->modulus) +
-	ssh2_bignum_length(rsa->exponent) +
-	ssh2_bignum_length(rsa->private_exponent) +
-	ssh2_bignum_length(rsa->iqmp) +
-	ssh2_bignum_length(rsa->p) + ssh2_bignum_length(rsa->q);
-
-    if (bloblen > len)
-	return bloblen;
-
-    bloblen = 0;
-#define ENC(x) \
-    PUT_32BIT(blob+bloblen, ssh2_bignum_length((x))-4); bloblen += 4; \
-    for (i = ssh2_bignum_length((x))-4; i-- ;) blob[bloblen++]=bignum_byte((x),i);
-    ENC(rsa->modulus);
-    ENC(rsa->exponent);
-    ENC(rsa->private_exponent);
-    ENC(rsa->iqmp);
-    ENC(rsa->p);
-    ENC(rsa->q);
-
-    return bloblen;
+    struct RSAKey *rsa = FROMFIELD(key, struct RSAKey, sshk);
+
+    put_mp_ssh2(bs, rsa->modulus);
+    put_mp_ssh2(bs, rsa->exponent);
+    put_mp_ssh2(bs, rsa->private_exponent);
+    put_mp_ssh2(bs, rsa->iqmp);
+    put_mp_ssh2(bs, rsa->p);
+    put_mp_ssh2(bs, rsa->q);
 }
 
-static int rsa2_pubkey_bits(const struct ssh_signkey *self,
-                            const void *blob, int len)
+static int rsa2_pubkey_bits(const ssh_keyalg *self, ptrlen pub)
 {
+    ssh_key *sshk;
     struct RSAKey *rsa;
     int ret;
 
-    rsa = rsa2_newkey(self, (const char *) blob, len);
-    if (!rsa)
-	return -1;
+    sshk = rsa2_new_pub(self, pub);
+    if (!sshk)
+        return -1;
+
+    rsa = FROMFIELD(sshk, struct RSAKey, sshk);
     ret = bignum_bitcount(rsa->modulus);
-    rsa2_freekey(rsa);
+    rsa2_freekey(&rsa->sshk);
 
     return ret;
 }
@@ -812,23 +647,32 @@ static const unsigned char asn1_weird_stuff[] = {
 
 #define ASN1_LEN ( (int) sizeof(asn1_weird_stuff) )
 
-static int rsa2_verifysig(void *key, const char *sig, int siglen,
-			  const char *data, int datalen)
+static int rsa2_verify(ssh_key *key, ptrlen sig, ptrlen data)
 {
-    struct RSAKey *rsa = (struct RSAKey *) key;
+    struct RSAKey *rsa = FROMFIELD(key, struct RSAKey, sshk);
+    BinarySource src[1];
+    ptrlen type, in_pl;
     Bignum in, out;
-    const char *p;
-    int slen;
     int bytes, i, j, ret;
     unsigned char hash[20];
 
-    getstring(&sig, &siglen, &p, &slen);
-    if (!p || slen != 7 || memcmp(p, "ssh-rsa", 7)) {
+    BinarySource_BARE_INIT(src, sig.ptr, sig.len);
+    type = get_string(src);
+    /*
+     * RFC 4253 section 6.6: the signature integer in an ssh-rsa
+     * signature is 'without lengths or padding'. That is, we _don't_
+     * expect the usual leading zero byte if the topmost bit of the
+     * first byte is set. (However, because of the possibility of
+     * BUG_SSH2_RSA_PADDING at the other end, we tolerate it if it's
+     * there.) So we can't use get_mp_ssh2, which enforces that
+     * leading-byte scheme; instead we use get_string and
+     * bignum_from_bytes, which will tolerate anything.
+     */
+    in_pl = get_string(src);
+    if (get_err(src) || !ptrlen_eq_string(type, "ssh-rsa"))
 	return 0;
-    }
-    in = getmp(&sig, &siglen);
-    if (!in)
-        return 0;
+
+    in = bignum_from_bytes(in_pl.ptr, in_pl.len);
     out = modpow(in, rsa->exponent, rsa->modulus);
     freebn(in);
 
@@ -852,7 +696,7 @@ static int rsa2_verifysig(void *key, const char *sig, int siglen,
 	    ret = 0;
     }
     /* Finally, we expect to see the SHA-1 hash of the signed data. */
-    SHA_Simple(data, datalen, hash);
+    SHA_Simple(data.ptr, data.len, hash);
     for (i = 19, j = 0; i >= 0; i--, j++) {
 	if (bignum_byte(out, i) != hash[j])
 	    ret = 0;
@@ -862,10 +706,10 @@ static int rsa2_verifysig(void *key, const char *sig, int siglen,
     return ret;
 }
 
-static unsigned char *rsa2_sign(void *key, const char *data, int datalen,
-				int *siglen)
+static void rsa2_sign(ssh_key *key, const void *data, int datalen,
+                      BinarySink *bs)
 {
-    struct RSAKey *rsa = (struct RSAKey *) key;
+    struct RSAKey *rsa = FROMFIELD(key, struct RSAKey, sshk);
     unsigned char *bytes;
     int nbytes;
     unsigned char hash[20];
@@ -892,51 +736,50 @@ static unsigned char *rsa2_sign(void *key, const char *data, int datalen,
     out = rsa_privkey_op(in, rsa);
     freebn(in);
 
+    put_stringz(bs, "ssh-rsa");
     nbytes = (bignum_bitcount(out) + 7) / 8;
-    bytes = snewn(4 + 7 + 4 + nbytes, unsigned char);
-    PUT_32BIT(bytes, 7);
-    memcpy(bytes + 4, "ssh-rsa", 7);
-    PUT_32BIT(bytes + 4 + 7, nbytes);
+    put_uint32(bs, nbytes);
     for (i = 0; i < nbytes; i++)
-	bytes[4 + 7 + 4 + i] = bignum_byte(out, nbytes - 1 - i);
-    freebn(out);
+	put_byte(bs, bignum_byte(out, nbytes - 1 - i));
 
-    *siglen = 4 + 7 + 4 + nbytes;
-    return bytes;
+    freebn(out);
 }
 
-const struct ssh_signkey ssh_rsa = {
-    rsa2_newkey,
+const ssh_keyalg ssh_rsa = {
+    rsa2_new_pub,
+    rsa2_new_priv,
+    rsa2_new_priv_openssh,
+
     rsa2_freekey,
-    rsa2_fmtkey,
+    rsa2_sign,
+    rsa2_verify,
     rsa2_public_blob,
     rsa2_private_blob,
-    rsa2_createkey,
-    rsa2_openssh_createkey,
-    rsa2_openssh_fmtkey,
-    6 /* n,e,d,iqmp,q,p */,
+    rsa2_openssh_blob,
+    rsa2_cache_str,
+
     rsa2_pubkey_bits,
-    rsa2_verifysig,
-    rsa2_sign,
+
     "ssh-rsa",
     "rsa2",
     NULL,
 };
 
-void *ssh_rsakex_newkey(char *data, int len)
+struct RSAKey *ssh_rsakex_newkey(const void *data, int len)
 {
-    return rsa2_newkey(&ssh_rsa, data, len);
+    ssh_key *sshk = rsa2_new_pub(&ssh_rsa, make_ptrlen(data, len));
+    if (!sshk)
+        return NULL;
+    return FROMFIELD(sshk, struct RSAKey, sshk);
 }
 
-void ssh_rsakex_freekey(void *key)
+void ssh_rsakex_freekey(struct RSAKey *key)
 {
-    rsa2_freekey(key);
+    rsa2_freekey(&key->sshk);
 }
 
-int ssh_rsakex_klen(void *key)
+int ssh_rsakex_klen(struct RSAKey *rsa)
 {
-    struct RSAKey *rsa = (struct RSAKey *) key;
-
     return bignum_bitcount(rsa->modulus);
 }
 
@@ -949,13 +792,14 @@ static void oaep_mask(const struct ssh_hash *h, void *seed, int seedlen,
     while (datalen > 0) {
         int i, max = (datalen > h->hlen ? h->hlen : datalen);
         void *s;
-        unsigned char counter[4], hash[SSH2_KEX_MAX_HASH_LEN];
+        BinarySink *bs;
+        unsigned char hash[SSH2_KEX_MAX_HASH_LEN];
 
 	assert(h->hlen <= SSH2_KEX_MAX_HASH_LEN);
-        PUT_32BIT(counter, count);
         s = h->init();
-        h->bytes(s, seed, seedlen);
-        h->bytes(s, counter, 4);
+        bs = h->sink(s);
+        put_data(bs, seed, seedlen);
+        put_uint32(bs, count);
         h->final(s, hash);
         count++;
 
@@ -968,11 +812,9 @@ static void oaep_mask(const struct ssh_hash *h, void *seed, int seedlen,
 }
 
 void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
-                        unsigned char *out, int outlen,
-                        void *key)
+                        unsigned char *out, int outlen, struct RSAKey *rsa)
 {
     Bignum b1, b2;
-    struct RSAKey *rsa = (struct RSAKey *) key;
     int k, i;
     char *p;
     const int HLEN = h->hlen;
@@ -1059,15 +901,12 @@ void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
      */
 }
 
-static const struct ssh_rsa_kex_extra ssh_rsa_kex_extra_sha1 = { 1024 };
-static const struct ssh_rsa_kex_extra ssh_rsa_kex_extra_sha256 = { 2048 };
-
 static const struct ssh_kex ssh_rsa_kex_sha1 = {
-    "rsa1024-sha1", NULL, KEXTYPE_RSA, &ssh_sha1, &ssh_rsa_kex_extra_sha1,
+    "rsa1024-sha1", NULL, KEXTYPE_RSA, &ssh_sha1, NULL,
 };
 
 static const struct ssh_kex ssh_rsa_kex_sha256 = {
-    "rsa2048-sha256", NULL, KEXTYPE_RSA, &ssh_sha256, &ssh_rsa_kex_extra_sha256,
+    "rsa2048-sha256", NULL, KEXTYPE_RSA, &ssh_sha256, NULL,
 };
 
 static const struct ssh_kex *const rsa_kex_list[] = {

+ 310 - 35
source/putty/sshsh256.c

@@ -5,6 +5,7 @@
  */
 
 #include "ssh.h"
+#include <assert.h>
 
 /* ----------------------------------------------------------------------
  * Core SHA256 algorithm: processes 16-word blocks into a message digest.
@@ -19,7 +20,11 @@
 #define smallsigma0(x) ( ror((x),7) ^ ror((x),18) ^ shr((x),3) )
 #define smallsigma1(x) ( ror((x),17) ^ ror((x),19) ^ shr((x),10) )
 
+static void SHA256_sw(SHA256_State *s, const unsigned char *q, int len);
+static void SHA256_ni(SHA256_State * s, const unsigned char *q, int len);
+
 #ifndef WINSCP_VS
+
 void SHA256_Core_Init(SHA256_State *s) {
     s->h[0] = 0x6a09e667;
     s->h[1] = 0xbb67ae85;
@@ -100,23 +105,40 @@ void SHA256_Block(SHA256_State *s, uint32 *block) {
 
 #define BLKSIZE 64
 
+static void SHA256_BinarySink_write(BinarySink *bs,
+                                    const void *p, size_t len);
+
 void SHA256_Init(SHA256_State *s) {
     SHA256_Core_Init(s);
     s->blkused = 0;
     s->lenhi = s->lenlo = 0;
+    if (supports_sha_ni())
+        s->sha256 = &SHA256_ni;
+    else
+        s->sha256 = &SHA256_sw;
+    BinarySink_INIT(s, SHA256_BinarySink_write);
 }
 
-void SHA256_Bytes(SHA256_State *s, const void *p, int len) {
+static void SHA256_BinarySink_write(BinarySink *bs,
+                                    const void *p, size_t len)
+{
+    struct SHA256_State *s = BinarySink_DOWNCAST(bs, struct SHA256_State);
     unsigned char *q = (unsigned char *)p;
-    uint32 wordblock[16];
+
     uint32 lenw = len;
-    int i;
+    assert(len == lenw);
 
     /*
      * Update the length field.
      */
     s->lenlo += lenw;
     s->lenhi += (s->lenlo < lenw);
+    (*(s->sha256))(s, q, len);
+}
+
+static void SHA256_sw(SHA256_State *s, const unsigned char *q, int len) {
+    uint32 wordblock[16];
+    int i;
 
     if (s->blkused && s->blkused+len < BLKSIZE) {
         /*
@@ -164,18 +186,10 @@ void SHA256_Final(SHA256_State *s, unsigned char *digest) {
 
     memset(c, 0, pad);
     c[0] = 0x80;
-    SHA256_Bytes(s, &c, pad);
+    put_data(s, &c, pad);
 
-    c[0] = (lenhi >> 24) & 0xFF;
-    c[1] = (lenhi >> 16) & 0xFF;
-    c[2] = (lenhi >>  8) & 0xFF;
-    c[3] = (lenhi >>  0) & 0xFF;
-    c[4] = (lenlo >> 24) & 0xFF;
-    c[5] = (lenlo >> 16) & 0xFF;
-    c[6] = (lenlo >>  8) & 0xFF;
-    c[7] = (lenlo >>  0) & 0xFF;
-
-    SHA256_Bytes(s, &c, 8);
+    put_uint32(s, lenhi);
+    put_uint32(s, lenlo);
 
     for (i = 0; i < 8; i++) {
 	digest[i*4+0] = (s->h[i] >> 24) & 0xFF;
@@ -189,7 +203,7 @@ void SHA256_Simple(const void *p, int len, unsigned char *output) {
     SHA256_State s;
 
     SHA256_Init(&s);
-    SHA256_Bytes(&s, p, len);
+    put_data(&s, p, len);
     SHA256_Final(&s, output);
     smemclr(&s, sizeof(s));
 }
@@ -214,6 +228,7 @@ static void *sha256_copy(const void *vold)
 
     s = snew(SHA256_State);
     *s = *old;
+    BinarySink_COPIED(s);
     return s;
 }
 
@@ -225,11 +240,10 @@ static void sha256_free(void *handle)
     sfree(s);
 }
 
-static void sha256_bytes(void *handle, const void *p, int len)
+static BinarySink *sha256_sink(void *handle)
 {
     SHA256_State *s = handle;
-
-    SHA256_Bytes(s, p, len);
+    return BinarySink_UPCAST(s);
 }
 
 static void sha256_final(void *handle, unsigned char *output)
@@ -241,7 +255,7 @@ static void sha256_final(void *handle, unsigned char *output)
 }
 
 const struct ssh_hash ssh_sha256 = {
-    sha256_init, sha256_copy, sha256_bytes, sha256_final, sha256_free,
+    sha256_init, sha256_copy, sha256_sink, sha256_final, sha256_free,
     32, "SHA-256"
 };
 
@@ -261,7 +275,8 @@ static void sha256_free_context(void *handle)
     sfree(handle);
 }
 
-static void sha256_key_internal(void *handle, unsigned char *key, int len)
+static void sha256_key_internal(void *handle,
+                                const unsigned char *key, int len)
 {
     SHA256_State *keys = (SHA256_State *)handle;
     unsigned char foo[64];
@@ -271,18 +286,18 @@ static void sha256_key_internal(void *handle, unsigned char *key, int len)
     for (i = 0; i < len && i < 64; i++)
 	foo[i] ^= key[i];
     SHA256_Init(&keys[0]);
-    SHA256_Bytes(&keys[0], foo, 64);
+    put_data(&keys[0], foo, 64);
 
     memset(foo, 0x5C, 64);
     for (i = 0; i < len && i < 64; i++)
 	foo[i] ^= key[i];
     SHA256_Init(&keys[1]);
-    SHA256_Bytes(&keys[1], foo, 64);
+    put_data(&keys[1], foo, 64);
 
     smemclr(foo, 64);		       /* burn the evidence */
 }
 
-static void sha256_key(void *handle, unsigned char *key)
+static void sha256_key(void *handle, const void *key)
 {
     sha256_key_internal(handle, key, 32);
 }
@@ -292,12 +307,13 @@ static void hmacsha256_start(void *handle)
     SHA256_State *keys = (SHA256_State *)handle;
 
     keys[2] = keys[0];		      /* structure copy */
+    BinarySink_COPIED(&keys[2]);
 }
 
-static void hmacsha256_bytes(void *handle, unsigned char const *blk, int len)
+static BinarySink *hmacsha256_sink(void *handle)
 {
     SHA256_State *keys = (SHA256_State *)handle;
-    SHA256_Bytes(&keys[2], (void *)blk, len);
+    return BinarySink_UPCAST(&keys[2]);
 }
 
 static void hmacsha256_genresult(void *handle, unsigned char *hmac)
@@ -307,27 +323,28 @@ static void hmacsha256_genresult(void *handle, unsigned char *hmac)
     unsigned char intermediate[32];
 
     s = keys[2];		       /* structure copy */
+    BinarySink_COPIED(&s);
     SHA256_Final(&s, intermediate);
     s = keys[1];		       /* structure copy */
-    SHA256_Bytes(&s, intermediate, 32);
+    BinarySink_COPIED(&s);
+    put_data(&s, intermediate, 32);
     SHA256_Final(&s, hmac);
 }
 
-static void sha256_do_hmac(void *handle, unsigned char *blk, int len,
+static void sha256_do_hmac(void *handle, const unsigned char *blk, int len,
 			 unsigned long seq, unsigned char *hmac)
 {
-    unsigned char seqbuf[4];
-
-    PUT_32BIT_MSB_FIRST(seqbuf, seq);
+    BinarySink *bs = hmacsha256_sink(handle);
     hmacsha256_start(handle);
-    hmacsha256_bytes(handle, seqbuf, 4);
-    hmacsha256_bytes(handle, blk, len);
+    put_uint32(bs, seq);
+    put_data(bs, blk, len);
     hmacsha256_genresult(handle, hmac);
 }
 
-static void sha256_generate(void *handle, unsigned char *blk, int len,
+static void sha256_generate(void *handle, void *vblk, int len,
 			  unsigned long seq)
 {
+    unsigned char *blk = (unsigned char *)vblk;
     sha256_do_hmac(handle, blk, len, seq, blk + len);
 }
 
@@ -338,9 +355,10 @@ static int hmacsha256_verresult(void *handle, unsigned char const *hmac)
     return smemeq(correct, hmac, 32);
 }
 
-static int sha256_verify(void *handle, unsigned char *blk, int len,
+static int sha256_verify(void *handle, const void *vblk, int len,
 		       unsigned long seq)
 {
+    const unsigned char *blk = (const unsigned char *)vblk;
     unsigned char correct[32];
     sha256_do_hmac(handle, blk, len, seq, correct);
     return smemeq(correct, blk + len, 32);
@@ -349,7 +367,7 @@ static int sha256_verify(void *handle, unsigned char *blk, int len,
 const struct ssh_mac ssh_hmac_sha256 = {
     sha256_make_context, sha256_free_context, sha256_key,
     sha256_generate, sha256_verify,
-    hmacsha256_start, hmacsha256_bytes,
+    hmacsha256_start, hmacsha256_sink,
     hmacsha256_genresult, hmacsha256_verresult,
     "hmac-sha2-256", "[email protected]",
     32, 32,
@@ -406,3 +424,260 @@ int main(void) {
 }
 
 #endif
+
+#ifdef COMPILER_SUPPORTS_SHA_NI
+
+#if defined _MSC_VER && defined _M_AMD64
+# include <intrin.h>
+#endif
+
+/*
+ * Set target architecture for Clang and GCC
+ */
+#if !defined(__clang__) && defined(__GNUC__)
+#    pragma GCC target("sha")
+#    pragma GCC target("sse4.1")
+#endif
+
+#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 5))
+#    define FUNC_ISA __attribute__ ((target("sse4.1,sha")))
+#else
+#    define FUNC_ISA
+#endif
+
+#include <wmmintrin.h>
+#include <smmintrin.h>
+#include <immintrin.h>
+
+#if defined(__clang__) || defined(__GNUC__)
+#include <shaintrin.h>
+#endif
+
+/* SHA256 implementation using new instructions
+   The code is based on Jeffrey Walton's SHA256 implementation:
+   https://github.com/noloader/SHA-Intrinsics
+*/
+FUNC_ISA
+static void SHA256_ni_(SHA256_State * s, const unsigned char *q, int len) {
+    if (s->blkused && s->blkused+len < BLKSIZE) {
+        /*
+         * Trivial case: just add to the block.
+         */
+        memcpy(s->block + s->blkused, q, len);
+        s->blkused += len;
+    } else {
+        __m128i STATE0, STATE1;
+        __m128i MSG, TMP;
+        __m128i MSG0, MSG1, MSG2, MSG3;
+        __m128i ABEF_SAVE, CDGH_SAVE;
+        const __m128i MASK = _mm_set_epi64x(0x0c0d0e0f08090a0bULL, 0x0405060700010203ULL);
+
+        /* Load initial values */
+        TMP = _mm_loadu_si128((const __m128i*) &s->h[0]);
+        STATE1 = _mm_loadu_si128((const __m128i*) &s->h[4]);
+
+        TMP = _mm_shuffle_epi32(TMP, 0xB1);          /* CDAB */
+        STATE1 = _mm_shuffle_epi32(STATE1, 0x1B);    /* EFGH */
+        STATE0 = _mm_alignr_epi8(TMP, STATE1, 8);    /* ABEF */
+        STATE1 = _mm_blend_epi16(STATE1, TMP, 0xF0); /* CDGH */
+        /*
+         * We must complete and process at least one block.
+         */
+        while (s->blkused + len >= BLKSIZE) {
+            memcpy(s->block + s->blkused, q, BLKSIZE - s->blkused);
+            q += BLKSIZE - s->blkused;
+            len -= BLKSIZE - s->blkused;
+
+                /* Save current state */
+            ABEF_SAVE = STATE0;
+            CDGH_SAVE = STATE1;
+
+            /* Rounds 0-3 */
+            MSG = _mm_loadu_si128((const __m128i*) (s->block + 0));
+            MSG0 = _mm_shuffle_epi8(MSG, MASK);
+            MSG = _mm_add_epi32(MSG0, _mm_set_epi64x(0xE9B5DBA5B5C0FBCFULL, 0x71374491428A2F98ULL));
+            STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+            MSG = _mm_shuffle_epi32(MSG, 0x0E);
+            STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+
+            /* Rounds 4-7 */
+            MSG1 = _mm_loadu_si128((const __m128i*) (s->block + 16));
+            MSG1 = _mm_shuffle_epi8(MSG1, MASK);
+            MSG = _mm_add_epi32(MSG1, _mm_set_epi64x(0xAB1C5ED5923F82A4ULL, 0x59F111F13956C25BULL));
+            STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+            MSG = _mm_shuffle_epi32(MSG, 0x0E);
+            STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+            MSG0 = _mm_sha256msg1_epu32(MSG0, MSG1);
+
+            /* Rounds 8-11 */
+            MSG2 = _mm_loadu_si128((const __m128i*) (s->block + 32));
+            MSG2 = _mm_shuffle_epi8(MSG2, MASK);
+            MSG = _mm_add_epi32(MSG2, _mm_set_epi64x(0x550C7DC3243185BEULL, 0x12835B01D807AA98ULL));
+            STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+            MSG = _mm_shuffle_epi32(MSG, 0x0E);
+            STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+            MSG1 = _mm_sha256msg1_epu32(MSG1, MSG2);
+
+            /* Rounds 12-15 */
+            MSG3 = _mm_loadu_si128((const __m128i*) (s->block + 48));
+            MSG3 = _mm_shuffle_epi8(MSG3, MASK);
+            MSG = _mm_add_epi32(MSG3, _mm_set_epi64x(0xC19BF1749BDC06A7ULL, 0x80DEB1FE72BE5D74ULL));
+            STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+            TMP = _mm_alignr_epi8(MSG3, MSG2, 4);
+            MSG0 = _mm_add_epi32(MSG0, TMP);
+            MSG0 = _mm_sha256msg2_epu32(MSG0, MSG3);
+            MSG = _mm_shuffle_epi32(MSG, 0x0E);
+            STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+            MSG2 = _mm_sha256msg1_epu32(MSG2, MSG3);
+
+            /* Rounds 16-19 */
+            MSG = _mm_add_epi32(MSG0, _mm_set_epi64x(0x240CA1CC0FC19DC6ULL, 0xEFBE4786E49B69C1ULL));
+            STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+            TMP = _mm_alignr_epi8(MSG0, MSG3, 4);
+            MSG1 = _mm_add_epi32(MSG1, TMP);
+            MSG1 = _mm_sha256msg2_epu32(MSG1, MSG0);
+            MSG = _mm_shuffle_epi32(MSG, 0x0E);
+            STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+            MSG3 = _mm_sha256msg1_epu32(MSG3, MSG0);
+
+            /* Rounds 20-23 */
+            MSG = _mm_add_epi32(MSG1, _mm_set_epi64x(0x76F988DA5CB0A9DCULL, 0x4A7484AA2DE92C6FULL));
+            STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+            TMP = _mm_alignr_epi8(MSG1, MSG0, 4);
+            MSG2 = _mm_add_epi32(MSG2, TMP);
+            MSG2 = _mm_sha256msg2_epu32(MSG2, MSG1);
+            MSG = _mm_shuffle_epi32(MSG, 0x0E);
+            STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+            MSG0 = _mm_sha256msg1_epu32(MSG0, MSG1);
+
+            /* Rounds 24-27 */
+            MSG = _mm_add_epi32(MSG2, _mm_set_epi64x(0xBF597FC7B00327C8ULL, 0xA831C66D983E5152ULL));
+            STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+            TMP = _mm_alignr_epi8(MSG2, MSG1, 4);
+            MSG3 = _mm_add_epi32(MSG3, TMP);
+            MSG3 = _mm_sha256msg2_epu32(MSG3, MSG2);
+            MSG = _mm_shuffle_epi32(MSG, 0x0E);
+            STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+            MSG1 = _mm_sha256msg1_epu32(MSG1, MSG2);
+
+            /* Rounds 28-31 */
+            MSG = _mm_add_epi32(MSG3, _mm_set_epi64x(0x1429296706CA6351ULL,  0xD5A79147C6E00BF3ULL));
+            STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+            TMP = _mm_alignr_epi8(MSG3, MSG2, 4);
+            MSG0 = _mm_add_epi32(MSG0, TMP);
+            MSG0 = _mm_sha256msg2_epu32(MSG0, MSG3);
+            MSG = _mm_shuffle_epi32(MSG, 0x0E);
+            STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+            MSG2 = _mm_sha256msg1_epu32(MSG2, MSG3);
+
+            /* Rounds 32-35 */
+            MSG = _mm_add_epi32(MSG0, _mm_set_epi64x(0x53380D134D2C6DFCULL, 0x2E1B213827B70A85ULL));
+            STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+            TMP = _mm_alignr_epi8(MSG0, MSG3, 4);
+            MSG1 = _mm_add_epi32(MSG1, TMP);
+            MSG1 = _mm_sha256msg2_epu32(MSG1, MSG0);
+            MSG = _mm_shuffle_epi32(MSG, 0x0E);
+            STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+            MSG3 = _mm_sha256msg1_epu32(MSG3, MSG0);
+
+            /* Rounds 36-39 */
+            MSG = _mm_add_epi32(MSG1, _mm_set_epi64x(0x92722C8581C2C92EULL, 0x766A0ABB650A7354ULL));
+            STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+            TMP = _mm_alignr_epi8(MSG1, MSG0, 4);
+            MSG2 = _mm_add_epi32(MSG2, TMP);
+            MSG2 = _mm_sha256msg2_epu32(MSG2, MSG1);
+            MSG = _mm_shuffle_epi32(MSG, 0x0E);
+            STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+            MSG0 = _mm_sha256msg1_epu32(MSG0, MSG1);
+
+            /* Rounds 40-43 */
+            MSG = _mm_add_epi32(MSG2, _mm_set_epi64x(0xC76C51A3C24B8B70ULL, 0xA81A664BA2BFE8A1ULL));
+            STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+            TMP = _mm_alignr_epi8(MSG2, MSG1, 4);
+            MSG3 = _mm_add_epi32(MSG3, TMP);
+            MSG3 = _mm_sha256msg2_epu32(MSG3, MSG2);
+            MSG = _mm_shuffle_epi32(MSG, 0x0E);
+            STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+            MSG1 = _mm_sha256msg1_epu32(MSG1, MSG2);
+
+            /* Rounds 44-47 */
+            MSG = _mm_add_epi32(MSG3, _mm_set_epi64x(0x106AA070F40E3585ULL, 0xD6990624D192E819ULL));
+            STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+            TMP = _mm_alignr_epi8(MSG3, MSG2, 4);
+            MSG0 = _mm_add_epi32(MSG0, TMP);
+            MSG0 = _mm_sha256msg2_epu32(MSG0, MSG3);
+            MSG = _mm_shuffle_epi32(MSG, 0x0E);
+            STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+            MSG2 = _mm_sha256msg1_epu32(MSG2, MSG3);
+
+            /* Rounds 48-51 */
+            MSG = _mm_add_epi32(MSG0, _mm_set_epi64x(0x34B0BCB52748774CULL, 0x1E376C0819A4C116ULL));
+            STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+            TMP = _mm_alignr_epi8(MSG0, MSG3, 4);
+            MSG1 = _mm_add_epi32(MSG1, TMP);
+            MSG1 = _mm_sha256msg2_epu32(MSG1, MSG0);
+            MSG = _mm_shuffle_epi32(MSG, 0x0E);
+            STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+            MSG3 = _mm_sha256msg1_epu32(MSG3, MSG0);
+
+            /* Rounds 52-55 */
+            MSG = _mm_add_epi32(MSG1, _mm_set_epi64x(0x682E6FF35B9CCA4FULL, 0x4ED8AA4A391C0CB3ULL));
+            STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+            TMP = _mm_alignr_epi8(MSG1, MSG0, 4);
+            MSG2 = _mm_add_epi32(MSG2, TMP);
+            MSG2 = _mm_sha256msg2_epu32(MSG2, MSG1);
+            MSG = _mm_shuffle_epi32(MSG, 0x0E);
+            STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+
+            /* Rounds 56-59 */
+            MSG = _mm_add_epi32(MSG2, _mm_set_epi64x(0x8CC7020884C87814ULL, 0x78A5636F748F82EEULL));
+            STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+            TMP = _mm_alignr_epi8(MSG2, MSG1, 4);
+            MSG3 = _mm_add_epi32(MSG3, TMP);
+            MSG3 = _mm_sha256msg2_epu32(MSG3, MSG2);
+            MSG = _mm_shuffle_epi32(MSG, 0x0E);
+            STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+
+            /* Rounds 60-63 */
+            MSG = _mm_add_epi32(MSG3, _mm_set_epi64x(0xC67178F2BEF9A3F7ULL, 0xA4506CEB90BEFFFAULL));
+            STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
+            MSG = _mm_shuffle_epi32(MSG, 0x0E);
+            STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
+
+            /* Combine state  */
+            STATE0 = _mm_add_epi32(STATE0, ABEF_SAVE);
+            STATE1 = _mm_add_epi32(STATE1, CDGH_SAVE);
+
+            s->blkused = 0;
+        }
+
+        TMP = _mm_shuffle_epi32(STATE0, 0x1B);       /* FEBA */
+        STATE1 = _mm_shuffle_epi32(STATE1, 0xB1);    /* DCHG */
+        STATE0 = _mm_blend_epi16(TMP, STATE1, 0xF0); /* DCBA */
+        STATE1 = _mm_alignr_epi8(STATE1, TMP, 8);    /* ABEF */
+
+        /* Save state */
+        _mm_storeu_si128((__m128i*) &s->h[0], STATE0);
+        _mm_storeu_si128((__m128i*) &s->h[4], STATE1);
+
+        memcpy(s->block, q, len);
+        s->blkused = len;
+    }
+}
+
+/*
+ * Workaround LLVM bug https://bugs.llvm.org/show_bug.cgi?id=34980
+ */
+static void SHA256_ni(SHA256_State * s, const unsigned char *q, int len)
+{
+    SHA256_ni_(s, q, len);
+}
+
+#else /* COMPILER_SUPPORTS_AES_NI */
+
+static void SHA256_ni(SHA256_State * s, const unsigned char *q, int len)
+{
+    assert(0);
+}
+
+#endif  /* COMPILER_SUPPORTS_AES_NI */

+ 23 - 19
source/putty/sshsh512.c

@@ -6,6 +6,7 @@
  * Modifications made for SHA-384 also
  */
 
+#include <assert.h>
 #include "ssh.h"
 
 #define BLKSIZE 128
@@ -185,12 +186,16 @@ static void SHA512_Block(SHA512_State *s, uint64 *block) {
  * at the end, and pass those blocks to the core SHA512 algorithm.
  */
 
+static void SHA512_BinarySink_write(BinarySink *bs,
+                                    const void *p, size_t len);
+
 void SHA512_Init(SHA512_State *s) {
     int i;
     SHA512_Core_Init(s);
     s->blkused = 0;
     for (i = 0; i < 4; i++)
 	s->len[i] = 0;
+    BinarySink_INIT(s, SHA512_BinarySink_write);
 }
 
 void SHA384_Init(SHA512_State *s) {
@@ -199,14 +204,20 @@ void SHA384_Init(SHA512_State *s) {
     s->blkused = 0;
     for (i = 0; i < 4; i++)
         s->len[i] = 0;
+    BinarySink_INIT(s, SHA512_BinarySink_write);
 }
 
-void SHA512_Bytes(SHA512_State *s, const void *p, int len) {
+static void SHA512_BinarySink_write(BinarySink *bs,
+                                    const void *p, size_t len)
+{
+    SHA512_State *s = BinarySink_DOWNCAST(bs, SHA512_State);
     unsigned char *q = (unsigned char *)p;
     uint64 wordblock[16];
     uint32 lenw = len;
     int i;
 
+    assert(lenw == len);
+
     /*
      * Update the length field.
      */
@@ -269,16 +280,10 @@ void SHA512_Final(SHA512_State *s, unsigned char *digest) {
 
     memset(c, 0, pad);
     c[0] = 0x80;
-    SHA512_Bytes(s, &c, pad);
+    put_data(s, &c, pad);
 
-    for (i = 0; i < 4; i++) {
-	c[i*4+0] = (len[3-i] >> 24) & 0xFF;
-	c[i*4+1] = (len[3-i] >> 16) & 0xFF;
-	c[i*4+2] = (len[3-i] >>  8) & 0xFF;
-	c[i*4+3] = (len[3-i] >>  0) & 0xFF;
-    }
-
-    SHA512_Bytes(s, &c, 16);
+    for (i = 0; i < 4; i++)
+        put_uint32(s, len[3-i]);
 
     for (i = 0; i < 8; i++) {
 	uint32 h, l;
@@ -304,7 +309,7 @@ void SHA512_Simple(const void *p, int len, unsigned char *output) {
     SHA512_State s;
 
     SHA512_Init(&s);
-    SHA512_Bytes(&s, p, len);
+    put_data(&s, p, len);
     SHA512_Final(&s, output);
     smemclr(&s, sizeof(s));
 }
@@ -313,7 +318,7 @@ void SHA384_Simple(const void *p, int len, unsigned char *output) {
     SHA512_State s;
 
     SHA384_Init(&s);
-    SHA512_Bytes(&s, p, len);
+    put_data(&s, p, len);
     SHA384_Final(&s, output);
     smemclr(&s, sizeof(s));
 }
@@ -338,6 +343,7 @@ static void *sha512_copy(const void *vold)
 
     s = snew(SHA512_State);
     *s = *old;
+    BinarySink_COPIED(s);
     return s;
 }
 
@@ -349,11 +355,10 @@ static void sha512_free(void *handle)
     sfree(s);
 }
 
-static void sha512_bytes(void *handle, const void *p, int len)
+static BinarySink *sha512_sink(void *handle)
 {
     SHA512_State *s = handle;
-
-    SHA512_Bytes(s, p, len);
+    return BinarySink_UPCAST(s);
 }
 
 static void sha512_final(void *handle, unsigned char *output)
@@ -365,7 +370,7 @@ static void sha512_final(void *handle, unsigned char *output)
 }
 
 const struct ssh_hash ssh_sha512 = {
-    sha512_init, sha512_copy, sha512_bytes, sha512_final, sha512_free,
+    sha512_init, sha512_copy, sha512_sink, sha512_final, sha512_free,
     64, "SHA-512"
 };
 
@@ -388,7 +393,7 @@ static void sha384_final(void *handle, unsigned char *output)
 }
 
 const struct ssh_hash ssh_sha384 = {
-    sha384_init, sha512_copy, sha512_bytes, sha384_final, sha512_free,
+    sha384_init, sha512_copy, sha512_sink, sha384_final, sha512_free,
     48, "SHA-384"
 };
 
@@ -450,8 +455,7 @@ int main(void) {
 	    int n;
 	    SHA512_Init(&s);
 	    for (n = 0; n < 1000000 / 40; n++)
-		SHA512_Bytes(&s, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
-			     40);
+		put_data(&s, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 40);
 	    SHA512_Final(&s, digest);
 	}
 	for (j = 0; j < 64; j++) {

+ 350 - 43
source/putty/sshsha.c

@@ -7,12 +7,17 @@
 
 #include "ssh.h"
 
+#include <assert.h>
+
 /* ----------------------------------------------------------------------
  * Core SHA algorithm: processes 16-word blocks into a message digest.
  */
 
 #define rol(x,y) ( ((x) << (y)) | (((uint32)x) >> (32-y)) )
 
+static void sha1_sw(SHA_State * s, const unsigned char *q, int len);
+static void sha1_ni(SHA_State * s, const unsigned char *q, int len);
+
 static void SHA_Core_Init(uint32 h[5])
 {
     h[0] = 0x67452301;
@@ -119,25 +124,39 @@ void SHATransform(word32 * digest, word32 * block)
  * the end, and pass those blocks to the core SHA algorithm.
  */
 
+static void SHA_BinarySink_write(BinarySink *bs, const void *p, size_t len);
+
 void SHA_Init(SHA_State * s)
 {
     SHA_Core_Init(s->h);
     s->blkused = 0;
     s->lenhi = s->lenlo = 0;
+    if (supports_sha_ni())
+        s->sha1 = &sha1_ni;
+    else
+        s->sha1 = &sha1_sw;
+    BinarySink_INIT(s, SHA_BinarySink_write);
 }
 
-void SHA_Bytes(SHA_State * s, const void *p, int len)
+static void SHA_BinarySink_write(BinarySink *bs, const void *p, size_t len)
 {
+    struct SHA_State *s = BinarySink_DOWNCAST(bs, struct SHA_State);
     const unsigned char *q = (const unsigned char *) p;
-    uint32 wordblock[16];
     uint32 lenw = len;
-    int i;
+    assert(lenw == len);
 
     /*
      * Update the length field.
      */
     s->lenlo += lenw;
     s->lenhi += (s->lenlo < lenw);
+    (*(s->sha1))(s, q, len);
+}
+
+static void sha1_sw(SHA_State * s, const unsigned char *q, int len)
+{
+    uint32 wordblock[16];
+    int i;
 
     if (s->blkused && s->blkused + len < 64) {
 	/*
@@ -189,18 +208,10 @@ void SHA_Final(SHA_State * s, unsigned char *output)
 
     memset(c, 0, pad);
     c[0] = 0x80;
-    SHA_Bytes(s, &c, pad);
-
-    c[0] = (lenhi >> 24) & 0xFF;
-    c[1] = (lenhi >> 16) & 0xFF;
-    c[2] = (lenhi >> 8) & 0xFF;
-    c[3] = (lenhi >> 0) & 0xFF;
-    c[4] = (lenlo >> 24) & 0xFF;
-    c[5] = (lenlo >> 16) & 0xFF;
-    c[6] = (lenlo >> 8) & 0xFF;
-    c[7] = (lenlo >> 0) & 0xFF;
+    put_data(s, &c, pad);
 
-    SHA_Bytes(s, &c, 8);
+    put_uint32(s, lenhi);
+    put_uint32(s, lenlo);
 
     for (i = 0; i < 5; i++) {
 	output[i * 4] = (s->h[i] >> 24) & 0xFF;
@@ -215,7 +226,7 @@ void SHA_Simple(const void *p, int len, unsigned char *output)
     SHA_State s;
 
     SHA_Init(&s);
-    SHA_Bytes(&s, p, len);
+    put_data(&s, p, len);
     SHA_Final(&s, output);
     smemclr(&s, sizeof(s));
 }
@@ -240,6 +251,7 @@ static void *sha1_copy(const void *vold)
 
     s = snew(SHA_State);
     *s = *old;
+    BinarySink_COPIED(s);
     return s;
 }
 
@@ -251,11 +263,10 @@ static void sha1_free(void *handle)
     sfree(s);
 }
 
-static void sha1_bytes(void *handle, const void *p, int len)
+static BinarySink *sha1_sink(void *handle)
 {
     SHA_State *s = handle;
-
-    SHA_Bytes(s, p, len);
+    return BinarySink_UPCAST(s);
 }
 
 static void sha1_final(void *handle, unsigned char *output)
@@ -267,7 +278,7 @@ static void sha1_final(void *handle, unsigned char *output)
 }
 
 const struct ssh_hash ssh_sha1 = {
-    sha1_init, sha1_copy, sha1_bytes, sha1_final, sha1_free, 20, "SHA-1"
+    sha1_init, sha1_copy, sha1_sink, sha1_final, sha1_free, 20, "SHA-1"
 };
 
 /* ----------------------------------------------------------------------
@@ -286,7 +297,7 @@ static void sha1_free_context(void *handle)
     sfree(handle);
 }
 
-static void sha1_key_internal(void *handle, unsigned char *key, int len)
+static void sha1_key_internal(void *handle, const unsigned char *key, int len)
 {
     SHA_State *keys = (SHA_State *)handle;
     unsigned char foo[64];
@@ -296,23 +307,23 @@ static void sha1_key_internal(void *handle, unsigned char *key, int len)
     for (i = 0; i < len && i < 64; i++)
 	foo[i] ^= key[i];
     SHA_Init(&keys[0]);
-    SHA_Bytes(&keys[0], foo, 64);
+    put_data(&keys[0], foo, 64);
 
     memset(foo, 0x5C, 64);
     for (i = 0; i < len && i < 64; i++)
 	foo[i] ^= key[i];
     SHA_Init(&keys[1]);
-    SHA_Bytes(&keys[1], foo, 64);
+    put_data(&keys[1], foo, 64);
 
     smemclr(foo, 64);		       /* burn the evidence */
 }
 
-static void sha1_key(void *handle, unsigned char *key)
+static void sha1_key(void *handle, const void *key)
 {
     sha1_key_internal(handle, key, 20);
 }
 
-static void sha1_key_buggy(void *handle, unsigned char *key)
+static void sha1_key_buggy(void *handle, const void *key)
 {
     sha1_key_internal(handle, key, 16);
 }
@@ -322,12 +333,13 @@ static void hmacsha1_start(void *handle)
     SHA_State *keys = (SHA_State *)handle;
 
     keys[2] = keys[0];		      /* structure copy */
+    BinarySink_COPIED(&keys[2]);
 }
 
-static void hmacsha1_bytes(void *handle, unsigned char const *blk, int len)
+static BinarySink *hmacsha1_sink(void *handle)
 {
     SHA_State *keys = (SHA_State *)handle;
-    SHA_Bytes(&keys[2], (void *)blk, len);
+    return BinarySink_UPCAST(&keys[2]);
 }
 
 static void hmacsha1_genresult(void *handle, unsigned char *hmac)
@@ -337,27 +349,28 @@ static void hmacsha1_genresult(void *handle, unsigned char *hmac)
     unsigned char intermediate[20];
 
     s = keys[2];		       /* structure copy */
+    BinarySink_COPIED(&s);
     SHA_Final(&s, intermediate);
     s = keys[1];		       /* structure copy */
-    SHA_Bytes(&s, intermediate, 20);
+    BinarySink_COPIED(&s);
+    put_data(&s, intermediate, 20);
     SHA_Final(&s, hmac);
 }
 
-static void sha1_do_hmac(void *handle, unsigned char *blk, int len,
+static void sha1_do_hmac(void *handle, const unsigned char *blk, int len,
 			 unsigned long seq, unsigned char *hmac)
 {
-    unsigned char seqbuf[4];
-
-    PUT_32BIT_MSB_FIRST(seqbuf, seq);
+    BinarySink *bs = hmacsha1_sink(handle);
     hmacsha1_start(handle);
-    hmacsha1_bytes(handle, seqbuf, 4);
-    hmacsha1_bytes(handle, blk, len);
+    put_uint32(bs, seq);
+    put_data(bs, blk, len);
     hmacsha1_genresult(handle, hmac);
 }
 
-static void sha1_generate(void *handle, unsigned char *blk, int len,
+static void sha1_generate(void *handle, void *vblk, int len,
 			  unsigned long seq)
 {
+    unsigned char *blk = (unsigned char *)vblk;
     sha1_do_hmac(handle, blk, len, seq, blk + len);
 }
 
@@ -368,9 +381,10 @@ static int hmacsha1_verresult(void *handle, unsigned char const *hmac)
     return smemeq(correct, hmac, 20);
 }
 
-static int sha1_verify(void *handle, unsigned char *blk, int len,
+static int sha1_verify(void *handle, const void *vblk, int len,
 		       unsigned long seq)
 {
+    const unsigned char *blk = (const unsigned char *)vblk;
     unsigned char correct[20];
     sha1_do_hmac(handle, blk, len, seq, correct);
     return smemeq(correct, blk + len, 20);
@@ -383,9 +397,10 @@ static void hmacsha1_96_genresult(void *handle, unsigned char *hmac)
     memcpy(hmac, full, 12);
 }
 
-static void sha1_96_generate(void *handle, unsigned char *blk, int len,
+static void sha1_96_generate(void *handle, void *vblk, int len,
 			     unsigned long seq)
 {
+    unsigned char *blk = (unsigned char *)vblk;
     unsigned char full[20];
     sha1_do_hmac(handle, blk, len, seq, full);
     memcpy(blk + len, full, 12);
@@ -398,9 +413,10 @@ static int hmacsha1_96_verresult(void *handle, unsigned char const *hmac)
     return smemeq(correct, hmac, 12);
 }
 
-static int sha1_96_verify(void *handle, unsigned char *blk, int len,
+static int sha1_96_verify(void *handle, const void *vblk, int len,
 		       unsigned long seq)
 {
+    const unsigned char *blk = (const unsigned char *)vblk;
     unsigned char correct[20];
     sha1_do_hmac(handle, blk, len, seq, correct);
     return smemeq(correct, blk + len, 12);
@@ -412,17 +428,17 @@ void hmac_sha1_simple(void *key, int keylen, void *data, int datalen,
     unsigned char intermediate[20];
 
     sha1_key_internal(states, key, keylen);
-    SHA_Bytes(&states[0], data, datalen);
+    put_data(&states[0], data, datalen);
     SHA_Final(&states[0], intermediate);
 
-    SHA_Bytes(&states[1], intermediate, 20);
+    put_data(&states[1], intermediate, 20);
     SHA_Final(&states[1], output);
 }
 
 const struct ssh_mac ssh_hmac_sha1 = {
     sha1_make_context, sha1_free_context, sha1_key,
     sha1_generate, sha1_verify,
-    hmacsha1_start, hmacsha1_bytes, hmacsha1_genresult, hmacsha1_verresult,
+    hmacsha1_start, hmacsha1_sink, hmacsha1_genresult, hmacsha1_verresult,
     "hmac-sha1", "[email protected]",
     20, 20,
     "HMAC-SHA1"
@@ -431,7 +447,7 @@ const struct ssh_mac ssh_hmac_sha1 = {
 const struct ssh_mac ssh_hmac_sha1_96 = {
     sha1_make_context, sha1_free_context, sha1_key,
     sha1_96_generate, sha1_96_verify,
-    hmacsha1_start, hmacsha1_bytes,
+    hmacsha1_start, hmacsha1_sink,
     hmacsha1_96_genresult, hmacsha1_96_verresult,
     "hmac-sha1-96", "[email protected]",
     12, 20,
@@ -441,7 +457,7 @@ const struct ssh_mac ssh_hmac_sha1_96 = {
 const struct ssh_mac ssh_hmac_sha1_buggy = {
     sha1_make_context, sha1_free_context, sha1_key_buggy,
     sha1_generate, sha1_verify,
-    hmacsha1_start, hmacsha1_bytes, hmacsha1_genresult, hmacsha1_verresult,
+    hmacsha1_start, hmacsha1_sink, hmacsha1_genresult, hmacsha1_verresult,
     "hmac-sha1", NULL,
     20, 16,
     "bug-compatible HMAC-SHA1"
@@ -450,9 +466,300 @@ const struct ssh_mac ssh_hmac_sha1_buggy = {
 const struct ssh_mac ssh_hmac_sha1_96_buggy = {
     sha1_make_context, sha1_free_context, sha1_key_buggy,
     sha1_96_generate, sha1_96_verify,
-    hmacsha1_start, hmacsha1_bytes,
+    hmacsha1_start, hmacsha1_sink,
     hmacsha1_96_genresult, hmacsha1_96_verresult,
     "hmac-sha1-96", NULL,
     12, 16,
     "bug-compatible HMAC-SHA1-96"
 };
+
+#ifdef COMPILER_SUPPORTS_SHA_NI
+
+#if defined _MSC_VER && defined _M_AMD64
+# include <intrin.h>
+#endif
+
+/*
+ * Set target architecture for Clang and GCC
+ */
+#if !defined(__clang__) && defined(__GNUC__)
+#    pragma GCC target("sha")
+#    pragma GCC target("sse4.1")
+#endif
+
+#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 5))
+#    define FUNC_ISA __attribute__ ((target("sse4.1,sha")))
+#else
+#    define FUNC_ISA
+#endif
+
+#include <wmmintrin.h>
+#include <smmintrin.h>
+#include <immintrin.h>
+
+#if defined(__clang__) || defined(__GNUC__)
+#include <shaintrin.h>
+#endif
+
+/*
+ * Determinators of CPU type
+ */
+#if defined(__clang__) || defined(__GNUC__)
+
+#include <cpuid.h>
+int supports_sha_ni(void)
+{
+    unsigned int CPUInfo[4];
+    __cpuid(0, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]);
+    if (CPUInfo[0] < 7)
+        return 0;
+
+    __cpuid_count(7, 0, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]);
+    return CPUInfo[1] & (1 << 29); /* SHA */
+}
+
+#else /* defined(__clang__) || defined(__GNUC__) */
+
+int supports_sha_ni(void)
+{
+    unsigned int CPUInfo[4];
+    __cpuid(CPUInfo, 0);  
+    if (CPUInfo[0] < 7)
+        return 0;
+
+    __cpuidex(CPUInfo, 7, 0);
+    return CPUInfo[1] & (1 << 29); /* Check SHA */
+}
+
+#endif /* defined(__clang__) || defined(__GNUC__) */
+
+/* SHA1 implementation using new instructions
+   The code is based on Jeffrey Walton's SHA1 implementation:
+   https://github.com/noloader/SHA-Intrinsics
+*/
+FUNC_ISA
+static void sha1_ni_(SHA_State * s, const unsigned char *q, int len)
+{
+    if (s->blkused && s->blkused + len < 64) {
+      /*
+       * Trivial case: just add to the block.
+       */
+       memcpy(s->block + s->blkused, q, len);
+       s->blkused += len;
+    } else {
+        __m128i ABCD, ABCD_SAVE, E0, E0_SAVE, E1;
+        const __m128i MASK = _mm_set_epi64x(0x0001020304050607ULL, 0x08090a0b0c0d0e0fULL);
+
+        ABCD = _mm_loadu_si128((const __m128i*) s->h);
+        E0 = _mm_set_epi32(s->h[4], 0, 0, 0);
+        ABCD = _mm_shuffle_epi32(ABCD, 0x1B);
+
+        /*
+         * We must complete and process at least one block.
+         */
+        while (s->blkused + len >= 64)
+        {
+            __m128i MSG0, MSG1, MSG2, MSG3;
+            memcpy(s->block + s->blkused, q, 64 - s->blkused);
+            q += 64 - s->blkused;
+            len -= 64 - s->blkused;
+
+            /* Save current state  */
+            ABCD_SAVE = ABCD;
+            E0_SAVE = E0;
+
+            /* Rounds 0-3 */
+            MSG0 = _mm_loadu_si128((const __m128i*)(s->block + 0));
+            MSG0 = _mm_shuffle_epi8(MSG0, MASK);
+            E0 = _mm_add_epi32(E0, MSG0);
+            E1 = ABCD;
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0);
+
+            /* Rounds 4-7 */
+            MSG1 = _mm_loadu_si128((const __m128i*)(s->block + 16));
+            MSG1 = _mm_shuffle_epi8(MSG1, MASK);
+            E1 = _mm_sha1nexte_epu32(E1, MSG1);
+            E0 = ABCD;
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 0);
+            MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1);
+
+            /* Rounds 8-11 */
+            MSG2 = _mm_loadu_si128((const __m128i*)(s->block + 32));
+            MSG2 = _mm_shuffle_epi8(MSG2, MASK);
+            E0 = _mm_sha1nexte_epu32(E0, MSG2);
+            E1 = ABCD;
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0);
+            MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2);
+            MSG0 = _mm_xor_si128(MSG0, MSG2);
+
+            /* Rounds 12-15 */
+            MSG3 = _mm_loadu_si128((const __m128i*)(s->block + 48));
+            MSG3 = _mm_shuffle_epi8(MSG3, MASK);
+            E1 = _mm_sha1nexte_epu32(E1, MSG3);
+            E0 = ABCD;
+            MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3);
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 0);
+            MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3);
+            MSG1 = _mm_xor_si128(MSG1, MSG3);
+
+            /* Rounds 16-19 */
+            E0 = _mm_sha1nexte_epu32(E0, MSG0);
+            E1 = ABCD;
+            MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0);
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0);
+            MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0);
+            MSG2 = _mm_xor_si128(MSG2, MSG0);
+
+            /* Rounds 20-23 */
+            E1 = _mm_sha1nexte_epu32(E1, MSG1);
+            E0 = ABCD;
+            MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1);
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1);
+            MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1);
+            MSG3 = _mm_xor_si128(MSG3, MSG1);
+
+            /* Rounds 24-27 */
+            E0 = _mm_sha1nexte_epu32(E0, MSG2);
+            E1 = ABCD;
+            MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2);
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 1);
+            MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2);
+            MSG0 = _mm_xor_si128(MSG0, MSG2);
+
+            /* Rounds 28-31 */
+            E1 = _mm_sha1nexte_epu32(E1, MSG3);
+            E0 = ABCD;
+            MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3);
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1);
+            MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3);
+            MSG1 = _mm_xor_si128(MSG1, MSG3);
+
+            /* Rounds 32-35 */
+            E0 = _mm_sha1nexte_epu32(E0, MSG0);
+            E1 = ABCD;
+            MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0);
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 1);
+            MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0);
+            MSG2 = _mm_xor_si128(MSG2, MSG0);
+
+            /* Rounds 36-39 */
+            E1 = _mm_sha1nexte_epu32(E1, MSG1);
+            E0 = ABCD;
+            MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1);
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1);
+            MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1);
+            MSG3 = _mm_xor_si128(MSG3, MSG1);
+
+            /* Rounds 40-43 */
+            E0 = _mm_sha1nexte_epu32(E0, MSG2);
+            E1 = ABCD;
+            MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2);
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2);
+            MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2);
+            MSG0 = _mm_xor_si128(MSG0, MSG2);
+
+            /* Rounds 44-47 */
+            E1 = _mm_sha1nexte_epu32(E1, MSG3);
+            E0 = ABCD;
+            MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3);
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 2);
+            MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3);
+            MSG1 = _mm_xor_si128(MSG1, MSG3);
+
+            /* Rounds 48-51 */
+            E0 = _mm_sha1nexte_epu32(E0, MSG0);
+            E1 = ABCD;
+            MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0);
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2);
+            MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0);
+            MSG2 = _mm_xor_si128(MSG2, MSG0);
+
+            /* Rounds 52-55 */
+            E1 = _mm_sha1nexte_epu32(E1, MSG1);
+            E0 = ABCD;
+            MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1);
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 2);
+            MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1);
+            MSG3 = _mm_xor_si128(MSG3, MSG1);
+
+            /* Rounds 56-59 */
+            E0 = _mm_sha1nexte_epu32(E0, MSG2);
+            E1 = ABCD;
+            MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2);
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2);
+            MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2);
+            MSG0 = _mm_xor_si128(MSG0, MSG2);
+
+            /* Rounds 60-63 */
+            E1 = _mm_sha1nexte_epu32(E1, MSG3);
+            E0 = ABCD;
+            MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3);
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3);
+            MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3);
+            MSG1 = _mm_xor_si128(MSG1, MSG3);
+
+            /* Rounds 64-67 */
+            E0 = _mm_sha1nexte_epu32(E0, MSG0);
+            E1 = ABCD;
+            MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0);
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 3);
+            MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0);
+            MSG2 = _mm_xor_si128(MSG2, MSG0);
+
+            /* Rounds 68-71 */
+            E1 = _mm_sha1nexte_epu32(E1, MSG1);
+            E0 = ABCD;
+            MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1);
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3);
+            MSG3 = _mm_xor_si128(MSG3, MSG1);
+
+            /* Rounds 72-75 */
+            E0 = _mm_sha1nexte_epu32(E0, MSG2);
+            E1 = ABCD;
+            MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2);
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 3);
+
+            /* Rounds 76-79 */
+            E1 = _mm_sha1nexte_epu32(E1, MSG3);
+            E0 = ABCD;
+            ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3);
+
+            /* Combine state */
+            E0 = _mm_sha1nexte_epu32(E0, E0_SAVE);
+            ABCD = _mm_add_epi32(ABCD, ABCD_SAVE);
+
+            s->blkused = 0;
+        }
+
+        ABCD = _mm_shuffle_epi32(ABCD, 0x1B);
+
+        /* Save state */
+        _mm_storeu_si128((__m128i*) s->h, ABCD);
+        s->h[4] = _mm_extract_epi32(E0, 3);
+
+        memcpy(s->block, q, len);
+        s->blkused = len;
+    }
+}
+
+/*
+ * Workaround LLVM bug https://bugs.llvm.org/show_bug.cgi?id=34980
+ */
+static void sha1_ni(SHA_State * s, const unsigned char *q, int len)
+{
+    sha1_ni_(s, q, len);
+}
+
+#else /* COMPILER_SUPPORTS_AES_NI */
+
+static void sha1_ni(SHA_State * s, const unsigned char *q, int len)
+{
+    assert(0);
+}
+
+int supports_sha_ni(void)
+{
+    return 0;
+}
+
+#endif  /* COMPILER_SUPPORTS_AES_NI */

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 275 - 338
source/putty/sshshare.c


+ 59 - 151
source/putty/sshzlib.c

@@ -41,6 +41,8 @@
 #include <string.h>
 #include <assert.h>
 
+#include "defs.h"
+
 #ifdef ZLIB_STANDALONE
 
 /*
@@ -57,13 +59,15 @@
 #define sresize(x, n, type) ( (type *) realloc((x), (n) * sizeof(type)) )
 #define sfree(x) ( free((x)) )
 
-#else
-#include "ssh.h"
-#endif
-
 #ifndef FALSE
 #define FALSE 0
-#define TRUE (!FALSE)
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#else
+#include "ssh.h"
 #endif
 
 /* ----------------------------------------------------------------------
@@ -370,7 +374,6 @@ struct Outbuf {
     unsigned long outbits;
     int noutbits;
     int firstblock;
-    int comp_disabled;
 };
 
 static void outbits(struct Outbuf *out, unsigned long bits, int nbits)
@@ -498,14 +501,6 @@ static void zlib_literal(struct LZ77Context *ectx, unsigned char c)
 {
     struct Outbuf *out = (struct Outbuf *) ectx->userdata;
 
-    if (out->comp_disabled) {
-	/*
-	 * We're in an uncompressed block, so just output the byte.
-	 */
-	outbits(out, c, 8);
-	return;
-    }
-
     if (c <= 143) {
 	/* 0 through 143 are 8 bits long starting at 00110000. */
 	outbits(out, mirrorbytes[0x30 + c], 8);
@@ -521,8 +516,6 @@ static void zlib_match(struct LZ77Context *ectx, int distance, int len)
     int i, j, k;
     struct Outbuf *out = (struct Outbuf *) ectx->userdata;
 
-    assert(!out->comp_disabled);
-
     while (len > 0) {
 	int thislen;
 
@@ -619,7 +612,6 @@ void *zlib_compress_init(void)
     out = snew(struct Outbuf);
     out->outbits = out->noutbits = 0;
     out->firstblock = 1;
-    out->comp_disabled = FALSE;
     ectx->userdata = out;
 
     return ectx;
@@ -633,50 +625,9 @@ void zlib_compress_cleanup(void *handle)
     sfree(ectx);
 }
 
-/*
- * Turn off actual LZ77 analysis for one block, to facilitate
- * construction of a precise-length IGNORE packet. Returns the
- * length adjustment (which is only valid for packets < 65536
- * bytes, but that seems reasonable enough).
- */
-static int zlib_disable_compression(void *handle)
-{
-    struct LZ77Context *ectx = (struct LZ77Context *)handle;
-    struct Outbuf *out = (struct Outbuf *) ectx->userdata;
-    int n;
-
-    out->comp_disabled = TRUE;
-
-    n = 0;
-    /*
-     * If this is the first block, we will start by outputting two
-     * header bytes, and then three bits to begin an uncompressed
-     * block. This will cost three bytes (because we will start on
-     * a byte boundary, this is certain).
-     */
-    if (out->firstblock) {
-	n = 3;
-    } else {
-	/*
-	 * Otherwise, we will output seven bits to close the
-	 * previous static block, and _then_ three bits to begin an
-	 * uncompressed block, and then flush the current byte.
-	 * This may cost two bytes or three, depending on noutbits.
-	 */
-	n += (out->noutbits + 10) / 8;
-    }
-
-    /*
-     * Now we output four bytes for the length / ~length pair in
-     * the uncompressed block.
-     */
-    n += 4;
-
-    return n;
-}
-
-int zlib_compress_block(void *handle, unsigned char *block, int len,
-			unsigned char **outblock, int *outlen)
+void zlib_compress_block(void *handle, unsigned char *block, int len,
+                         unsigned char **outblock, int *outlen,
+                         int minlen)
 {
     struct LZ77Context *ectx = (struct LZ77Context *)handle;
     struct Outbuf *out = (struct Outbuf *) ectx->userdata;
@@ -698,102 +649,60 @@ int zlib_compress_block(void *handle, unsigned char *block, int len,
     } else
 	in_block = TRUE;
 
-    if (out->comp_disabled) {
-	if (in_block)
-	    outbits(out, 0, 7);	       /* close static block */
-
-	while (len > 0) {
-	    int blen = (len < 65535 ? len : 65535);
-
-	    /*
-	     * Start a Deflate (RFC1951) uncompressed block. We
-	     * transmit a zero bit (BFINAL=0), followed by two more
-	     * zero bits (BTYPE=00). Of course these are in the
-	     * wrong order (00 0), not that it matters.
-	     */
-	    outbits(out, 0, 3);
-
-	    /*
-	     * Output zero bits to align to a byte boundary.
-	     */
-	    if (out->noutbits)
-		outbits(out, 0, 8 - out->noutbits);
-
-	    /*
-	     * Output the block length, and then its one's
-	     * complement. They're little-endian, so all we need to
-	     * do is pass them straight to outbits() with bit count
-	     * 16.
-	     */
-	    outbits(out, blen, 16);
-	    outbits(out, blen ^ 0xFFFF, 16);
-
-	    /*
-	     * Do the `compression': we need to pass the data to
-	     * lz77_compress so that it will be taken into account
-	     * for subsequent (distance,length) pairs. But
-	     * lz77_compress is passed FALSE, which means it won't
-	     * actually find (or even look for) any matches; so
-	     * every character will be passed straight to
-	     * zlib_literal which will spot out->comp_disabled and
-	     * emit in the uncompressed format.
-	     */
-	    lz77_compress(ectx, block, blen, FALSE);
+    if (!in_block) {
+        /*
+         * Start a Deflate (RFC1951) fixed-trees block. We
+         * transmit a zero bit (BFINAL=0), followed by a zero
+         * bit and a one bit (BTYPE=01). Of course these are in
+         * the wrong order (01 0).
+         */
+        outbits(out, 2, 3);
+    }
 
-	    len -= blen;
-	    block += blen;
-	}
-	outbits(out, 2, 3);	       /* open new block */
-    } else {
-	if (!in_block) {
-	    /*
-	     * Start a Deflate (RFC1951) fixed-trees block. We
-	     * transmit a zero bit (BFINAL=0), followed by a zero
-	     * bit and a one bit (BTYPE=01). Of course these are in
-	     * the wrong order (01 0).
-	     */
-	    outbits(out, 2, 3);
-	}
+    /*
+     * Do the compression.
+     */
+    lz77_compress(ectx, block, len, TRUE);
 
-	/*
-	 * Do the compression.
-	 */
-	lz77_compress(ectx, block, len, TRUE);
+    /*
+     * End the block (by transmitting code 256, which is
+     * 0000000 in fixed-tree mode), and transmit some empty
+     * blocks to ensure we have emitted the byte containing the
+     * last piece of genuine data. There are three ways we can
+     * do this:
+     *
+     *  - Minimal flush. Output end-of-block and then open a
+     *    new static block. This takes 9 bits, which is
+     *    guaranteed to flush out the last genuine code in the
+     *    closed block; but allegedly zlib can't handle it.
+     *
+     *  - Zlib partial flush. Output EOB, open and close an
+     *    empty static block, and _then_ open the new block.
+     *    This is the best zlib can handle.
+     *
+     *  - Zlib sync flush. Output EOB, then an empty
+     *    _uncompressed_ block (000, then sync to byte
+     *    boundary, then send bytes 00 00 FF FF). Then open the
+     *    new block.
+     *
+     * For the moment, we will use Zlib partial flush.
+     */
+    outbits(out, 0, 7);	       /* close block */
+    outbits(out, 2, 3 + 7);    /* empty static block */
+    outbits(out, 2, 3);	       /* open new block */
 
-	/*
-	 * End the block (by transmitting code 256, which is
-	 * 0000000 in fixed-tree mode), and transmit some empty
-	 * blocks to ensure we have emitted the byte containing the
-	 * last piece of genuine data. There are three ways we can
-	 * do this:
-	 *
-	 *  - Minimal flush. Output end-of-block and then open a
-	 *    new static block. This takes 9 bits, which is
-	 *    guaranteed to flush out the last genuine code in the
-	 *    closed block; but allegedly zlib can't handle it.
-	 *
-	 *  - Zlib partial flush. Output EOB, open and close an
-	 *    empty static block, and _then_ open the new block.
-	 *    This is the best zlib can handle.
-	 *
-	 *  - Zlib sync flush. Output EOB, then an empty
-	 *    _uncompressed_ block (000, then sync to byte
-	 *    boundary, then send bytes 00 00 FF FF). Then open the
-	 *    new block.
-	 *
-	 * For the moment, we will use Zlib partial flush.
-	 */
-	outbits(out, 0, 7);	       /* close block */
-	outbits(out, 2, 3 + 7);	       /* empty static block */
-	outbits(out, 2, 3);	       /* open new block */
+    /*
+     * If we've been asked to pad out the compressed data until it's
+     * at least a given length, do so by emitting further empty static
+     * blocks.
+     */
+    while (out->outlen < minlen) {
+        outbits(out, 0, 7);	       /* close block */
+        outbits(out, 2, 3);	       /* open new static block */
     }
 
-    out->comp_disabled = FALSE;
-
     *outblock = out->outbuf;
     *outlen = out->outlen;
-
-    return 1;
 }
 
 /* ----------------------------------------------------------------------
@@ -1389,7 +1298,6 @@ const struct ssh_compress ssh_zlib = {
     zlib_decompress_init,
     zlib_decompress_cleanup,
     zlib_decompress_block,
-    zlib_disable_compression,
     "zlib (RFC1950)"
 };
 

+ 35 - 6
source/putty/version.h

@@ -1,6 +1,35 @@
-/* Generated by automated build script */
-#define RELEASE 0.70
-#define TEXTVER "Release 0.70+"
-#define SSHVER "PuTTY-Release-0.70"
-#define BINARY_VERSION 0,70,0,0
-#define SOURCE_COMMIT "3cd10509a51edf5a21cdc80aabf7e6a934522d47"
+/*
+ * This header file provides the various versioning-related #defines
+ * for a particular PuTTY build.
+ *
+ * When my automated build system does a full build, Buildscr
+ * completely overwrites this file with information derived from the
+ * circumstances and type of that build. The information _here_ is
+ * default stuff used for local development runs of 'make'.
+ */
+
+#define TEXTVER "Unidentified build"
+#define SSHVER "PuTTY-Unidentified-Local-Build"
+#define BINARY_VERSION 0,0,0,0
+
+#ifndef SOURCE_COMMIT
+/*
+ * git commit id from which this build was made. This is defined by
+ * Buildscr for official builds - both source archives and prebuilt
+ * binaries - in the course of overwriting this file as described
+ * above. But we put it here under ifdef, so that it can also be
+ * passed in on the command line for Unix local development builds,
+ * which I treat specially because Unix developers - e.g. me - are
+ * quite likely to run 'make install' straight out of their dev
+ * directory so as to use the bleeding-edge code for day-to-day
+ * running.
+ *
+ * Windows doesn't really need the same treatment, because the easiest
+ * way to install a build properly on Windows is to run the installer,
+ * and the easiest way to do that is to run Buildscr, which will
+ * populate this field its own way. It's only the Unix automake build
+ * where you might go straight from local 'make' to 'make install'
+ * without going through Buildscr.
+ */
+#define SOURCE_COMMIT "unavailable"
+#endif

+ 139 - 12
source/putty/windows/wingss.c

@@ -1,5 +1,6 @@
 #ifndef NO_GSSAPI
 
+#include <limits.h>
 #include "putty.h"
 
 #define SECURITY_WIN32
@@ -11,11 +12,33 @@
 
 #include "misc.h"
 
+#define UNIX_EPOCH	11644473600ULL	/* Seconds from Windows epoch */
+#define CNS_PERSEC	10000000ULL	/* # 100ns per second */
+
+/*
+ * Note, as a special case, 0 relative to the Windows epoch (unspecified) maps
+ * to 0 relative to the POSIX epoch (unspecified)!
+ */
+#define TIME_WIN_TO_POSIX(ft, t) do { \
+    ULARGE_INTEGER uli; \
+    uli.LowPart  = (ft).dwLowDateTime; \
+    uli.HighPart = (ft).dwHighDateTime; \
+    if (uli.QuadPart != 0) \
+        uli.QuadPart = uli.QuadPart / CNS_PERSEC - UNIX_EPOCH; \
+    (t) = (time_t) uli.QuadPart; \
+} while(0)
+
 /* Windows code to set up the GSSAPI library list. */
 
+#ifdef _WIN64
+#define MIT_KERB_SUFFIX "64"
+#else
+#define MIT_KERB_SUFFIX "32"
+#endif
+
 const int ngsslibs = 3;
 const char *const gsslibnames[3] = {
-    "MIT Kerberos GSSAPI32.DLL",
+    "MIT Kerberos GSSAPI"MIT_KERB_SUFFIX".DLL",
     "Microsoft SSPI SECUR32.DLL",
     "User-specified GSSAPI DLL",
 };
@@ -27,7 +50,7 @@ const struct keyvalwhere gsslibkeywords[] = {
 
 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
 		      AcquireCredentialsHandleA,
-		      (SEC_CHAR *, SEC_CHAR *, ULONG, PLUID,
+		      (SEC_CHAR *, SEC_CHAR *, ULONG, PVOID,
 		       PVOID, SEC_GET_KEY_FN, PVOID, PCredHandle, PTimeStamp));
 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
 		      InitializeSecurityContextA,
@@ -49,6 +72,9 @@ DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
 		      MakeSignature,
 		      (PCtxtHandle, ULONG, PSecBufferDesc, ULONG));
+DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
+                      VerifySignature,
+                      (PCtxtHandle, PSecBufferDesc, ULONG, PULONG));
 DECL_WINDOWS_FUNCTION(static, DLL_DIRECTORY_COOKIE,
                       AddDllDirectory,
                       (PCWSTR));
@@ -93,7 +119,6 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf, void *frontend) // MPEXT
     list->nlibraries = 0;
 
     /* MIT Kerberos GSSAPI implementation */
-    /* TODO: For 64-bit builds, check for gssapi64.dll */
     module = NULL;
     if (RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\MIT\\Kerberos", &regkey)
 	== ERROR_SUCCESS) {
@@ -118,7 +143,7 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf, void *frontend) // MPEXT
                     p_AddDllDirectory(dllPath);
                     sfree(dllPath);
                 }
-                strcat (buffer, "\\gssapi32.dll");
+                strcat (buffer, "\\gssapi"MIT_KERB_SUFFIX".dll");
                 module = LoadLibraryEx (buffer, NULL,
                                         LOAD_LIBRARY_SEARCH_SYSTEM32 |
                                         LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR |
@@ -133,7 +158,7 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf, void *frontend) // MPEXT
 	    &list->libraries[list->nlibraries++];
 
 	lib->id = 0;
-	lib->gsslogmsg = "Using GSSAPI from GSSAPI32.DLL";
+	lib->gsslogmsg = "Using GSSAPI from GSSAPI"MIT_KERB_SUFFIX".DLL";
 	lib->handle = (void *)module;
 
 #define BIND_GSS_FN(name) \
@@ -142,6 +167,7 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf, void *frontend) // MPEXT
         BIND_GSS_FN(delete_sec_context);
         BIND_GSS_FN(display_status);
         BIND_GSS_FN(get_mic);
+        BIND_GSS_FN(verify_mic);
         BIND_GSS_FN(import_name);
         BIND_GSS_FN(init_sec_context);
         BIND_GSS_FN(release_buffer);
@@ -172,6 +198,7 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf, void *frontend) // MPEXT
 	GET_WINDOWS_FUNCTION(module, DeleteSecurityContext);
 	GET_WINDOWS_FUNCTION(module, QueryContextAttributesA);
 	GET_WINDOWS_FUNCTION(module, MakeSignature);
+        GET_WINDOWS_FUNCTION(module, VerifySignature);
 
 	ssh_sspi_bind_fns(lib);
     }
@@ -230,6 +257,7 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf, void *frontend) // MPEXT
         BIND_GSS_FN(delete_sec_context);
         BIND_GSS_FN(display_status);
         BIND_GSS_FN(get_mic);
+        BIND_GSS_FN(verify_mic);
         BIND_GSS_FN(import_name);
         BIND_GSS_FN(init_sec_context);
         BIND_GSS_FN(release_buffer);
@@ -295,7 +323,8 @@ static Ssh_gss_stat ssh_sspi_import_name(struct ssh_gss_library *lib,
 }
 
 static Ssh_gss_stat ssh_sspi_acquire_cred(struct ssh_gss_library *lib,
-					  Ssh_gss_ctx *ctx)
+                                          Ssh_gss_ctx *ctx,
+                                          time_t *expiry)
 {
     winSsh_gss_ctx *winctx = snew(winSsh_gss_ctx);
     memset(winctx, 0, sizeof(winSsh_gss_ctx));
@@ -315,10 +344,18 @@ static Ssh_gss_stat ssh_sspi_acquire_cred(struct ssh_gss_library *lib,
 						   NULL,
 						   NULL,
 						   &winctx->cred_handle,
-						   &winctx->expiry);
+                                                   NULL);
+
+    if (winctx->maj_stat != SEC_E_OK) {
+        p_FreeCredentialsHandle(&winctx->cred_handle);
+        sfree(winctx);
+        return SSH_GSS_FAILURE;
+    }
+
+    /* Windows does not return a valid expiration from AcquireCredentials */
+    if (expiry)
+        *expiry = GSS_NO_EXPIRATION;
 
-    if (winctx->maj_stat != SEC_E_OK) return SSH_GSS_FAILURE;
-    
     *ctx = (Ssh_gss_ctx) winctx;
     return SSH_GSS_OK;
 }
@@ -355,12 +392,68 @@ static SecBufferDesc ssh_gss_init_sec_buffer_desc(unsigned long version,
 #define MPEXT_INIT_SEC_BUFFERDESC(VERSION, BUFFERSCOUNT, BUFFERS) \
   {VERSION, BUFFERSCOUNT, BUFFERS}
 #endif
+
+static void localexp_to_exp_lifetime(TimeStamp *localexp,
+                                     time_t *expiry, unsigned long *lifetime)
+{
+    FILETIME nowUTC;
+    FILETIME expUTC;
+    time_t now;
+    time_t exp;
+    time_t delta;
+
+    if (!lifetime && !expiry)
+        return;
+
+    GetSystemTimeAsFileTime(&nowUTC);
+    TIME_WIN_TO_POSIX(nowUTC, now);
+
+    if (lifetime)
+        *lifetime = 0;
+    if (expiry)
+        *expiry = GSS_NO_EXPIRATION;
+
+    /*
+     * Type oddity: localexp is a pointer to 'TimeStamp', whereas
+     * LocalFileTimeToFileTime expects a pointer to FILETIME. However,
+     * despite having different formal type names from the compiler's
+     * point of view, these two structures are specified to be
+     * isomorphic in the MS documentation, so it's legitimate to copy
+     * between them:
+     *
+     * https://msdn.microsoft.com/en-us/library/windows/desktop/aa380511(v=vs.85).aspx
+     */
+    {
+        FILETIME localexp_ft;
+        enum { vorpal_sword = 1 / (sizeof(*localexp) == sizeof(localexp_ft)) };
+        memcpy(&localexp_ft, localexp, sizeof(localexp_ft));
+        if (!LocalFileTimeToFileTime(&localexp_ft, &expUTC))
+            return;
+    }
+
+    TIME_WIN_TO_POSIX(expUTC, exp);
+    delta = exp - now;
+    if (exp == 0 || delta <= 0)
+        return;
+
+    if (expiry)
+        *expiry = exp;
+    if (lifetime) {
+        if (delta <= ULONG_MAX)
+            *lifetime = (unsigned long)delta;
+        else
+            *lifetime = ULONG_MAX;
+    }
+}
+
 static Ssh_gss_stat ssh_sspi_init_sec_context(struct ssh_gss_library *lib,
 					      Ssh_gss_ctx *ctx,
 					      Ssh_gss_name srv_name,
 					      int to_deleg,
 					      Ssh_gss_buf *recv_tok,
-					      Ssh_gss_buf *send_tok)
+                                              Ssh_gss_buf *send_tok,
+                                              time_t *expiry,
+                                              unsigned long *lifetime)
 {
     winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) *ctx;
     SecBuffer wsend_tok = MPEXT_INIT_SEC_BUFFER(send_tok->length,SECBUFFER_TOKEN,send_tok->value);
@@ -370,6 +463,7 @@ static Ssh_gss_stat ssh_sspi_init_sec_context(struct ssh_gss_library *lib,
     unsigned long flags=ISC_REQ_MUTUAL_AUTH|ISC_REQ_REPLAY_DETECT|
 	ISC_REQ_CONFIDENTIALITY|ISC_REQ_ALLOCATE_MEMORY;
     unsigned long ret_flags=0;
+    TimeStamp localexp;
     
     /* check if we have to delegate ... */
     if (to_deleg) flags |= ISC_REQ_DELEGATE;
@@ -384,8 +478,10 @@ static Ssh_gss_stat ssh_sspi_init_sec_context(struct ssh_gss_library *lib,
 						    &winctx->context,
 						    &output_desc,
 						    &ret_flags,
-						    &winctx->expiry);
-  
+                                                    &localexp);
+
+    localexp_to_exp_lifetime(&localexp, expiry, lifetime);
+
     /* prepare for the next round */
     winctx->context_handle = &winctx->context;
     send_tok->value = wsend_tok.pvBuffer;
@@ -543,6 +639,36 @@ static Ssh_gss_stat ssh_sspi_get_mic(struct ssh_gss_library *lib,
     return winctx->maj_stat;
 }
 
+static Ssh_gss_stat ssh_sspi_verify_mic(struct ssh_gss_library *lib,
+                                        Ssh_gss_ctx ctx,
+                                        Ssh_gss_buf *buf,
+                                        Ssh_gss_buf *mic)
+{
+    winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) ctx;
+    SecBufferDesc InputBufferDescriptor;
+    SecBuffer InputSecurityToken[2];
+    ULONG qop;
+
+    if (winctx == NULL) return SSH_GSS_FAILURE;
+
+    winctx->maj_stat = 0;
+
+    InputBufferDescriptor.cBuffers = 2;
+    InputBufferDescriptor.pBuffers = InputSecurityToken;
+    InputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
+    InputSecurityToken[0].BufferType = SECBUFFER_DATA;
+    InputSecurityToken[0].cbBuffer = buf->length;
+    InputSecurityToken[0].pvBuffer = buf->value;
+    InputSecurityToken[1].BufferType = SECBUFFER_TOKEN;
+    InputSecurityToken[1].cbBuffer = mic->length;
+    InputSecurityToken[1].pvBuffer = mic->value;
+
+    winctx->maj_stat = p_VerifySignature(&winctx->context,
+                                       &InputBufferDescriptor,
+                                       0, &qop);
+    return winctx->maj_stat;
+}
+
 static Ssh_gss_stat ssh_sspi_free_mic(struct ssh_gss_library *lib,
 				      Ssh_gss_buf *hash)
 {
@@ -560,6 +686,7 @@ static void ssh_sspi_bind_fns(struct ssh_gss_library *lib)
     lib->acquire_cred = ssh_sspi_acquire_cred;
     lib->release_cred = ssh_sspi_release_cred;
     lib->get_mic = ssh_sspi_get_mic;
+    lib->verify_mic = ssh_sspi_verify_mic;
     lib->free_mic = ssh_sspi_free_mic;
     lib->display_status = ssh_sspi_display_status;
 }

+ 102 - 105
source/putty/windows/winhsock.c

@@ -12,16 +12,11 @@
 #include "putty.h"
 #include "network.h"
 
-typedef struct Socket_handle_tag *Handle_Socket;
-
 #ifdef MPEXT
 extern char *do_select(Plug plug, SOCKET skt, int startup);
 #endif
 
-struct Socket_handle_tag {
-    const struct socket_function_table *fn;
-    /* the above variable absolutely *must* be the first in this structure */
-
+typedef struct HandleSocket {
     HANDLE send_H, recv_H, stderr_H;
     struct handle *send_h, *recv_h, *stderr_h;
 
@@ -51,29 +46,31 @@ struct Socket_handle_tag {
     char *error;
 
     Plug plug;
-};
+
+    const Socket_vtable *sockvt;
+} HandleSocket;
 
 static int handle_gotdata(struct handle *h, void *data, int len)
 {
-    Handle_Socket ps = (Handle_Socket) handle_get_privdata(h);
+    HandleSocket *hs = (HandleSocket *)handle_get_privdata(h);
 
     if (len < 0) {
-	plug_closing(ps->plug, "Read error from handle", 0, 0);
+	plug_closing(hs->plug, "Read error from handle", 0, 0);
 	return 0;
     } else if (len == 0) {
-	plug_closing(ps->plug, NULL, 0, 0);
+	plug_closing(hs->plug, NULL, 0, 0);
 	return 0;
     } else {
-        assert(ps->frozen != FROZEN && ps->frozen != THAWING);
-        if (ps->frozen == FREEZING) {
+        assert(hs->frozen != FROZEN && hs->frozen != THAWING);
+        if (hs->frozen == FREEZING) {
             /*
              * If we've received data while this socket is supposed to
              * be frozen (because the read winhandl.c started before
              * sk_set_frozen was called has now returned) then buffer
              * the data for when we unfreeze.
              */
-            bufchain_add(&ps->inputdata, data, len);
-            ps->frozen = FROZEN;
+            bufchain_add(&hs->inputdata, data, len);
+            hs->frozen = FROZEN;
 
             /*
              * And return a very large backlog, to prevent further
@@ -81,7 +78,7 @@ static int handle_gotdata(struct handle *h, void *data, int len)
              */
             return INT_MAX;
         } else {
-            plug_receive(ps->plug, 0, data, len);
+            plug_receive(hs->plug, 0, data, len);
 	    return 0;
         }
     }
@@ -89,11 +86,11 @@ static int handle_gotdata(struct handle *h, void *data, int len)
 
 static int handle_stderr(struct handle *h, void *data, int len)
 {
-    Handle_Socket ps = (Handle_Socket) handle_get_privdata(h);
+    HandleSocket *hs = (HandleSocket *)handle_get_privdata(h);
 
     if (len > 0)
     {
-        log_proxy_stderr(ps->plug, &ps->stderrdata, data, len);
+        log_proxy_stderr(hs->plug, &hs->stderrdata, data, len);
     }
 
     return 0;
@@ -101,71 +98,71 @@ static int handle_stderr(struct handle *h, void *data, int len)
 
 static void handle_sentdata(struct handle *h, int new_backlog)
 {
-    Handle_Socket ps = (Handle_Socket) handle_get_privdata(h);
+    HandleSocket *hs = (HandleSocket *)handle_get_privdata(h);
 
     if (new_backlog < 0) {
         /* Special case: this is actually reporting an error writing
          * to the underlying handle, and our input value is the error
          * code itself, negated. */
-        plug_closing(ps->plug, win_strerror(-new_backlog), -new_backlog, 0);
+        plug_closing(hs->plug, win_strerror(-new_backlog), -new_backlog, 0);
         return;
     }
 
-    plug_sent(ps->plug, new_backlog);
+    plug_sent(hs->plug, new_backlog);
 }
 
 static Plug sk_handle_plug(Socket s, Plug p)
 {
-    Handle_Socket ps = (Handle_Socket) s;
-    Plug ret = ps->plug;
+    HandleSocket *hs = FROMFIELD(s, HandleSocket, sockvt);
+    Plug ret = hs->plug;
     if (p)
-	ps->plug = p;
+	hs->plug = p;
     return ret;
 }
 
 static void sk_handle_close(Socket s)
 {
-    Handle_Socket ps = (Handle_Socket) s;
+    HandleSocket *hs = FROMFIELD(s, HandleSocket, sockvt);
 
-    if (ps->defer_close) {
-        ps->deferred_close = TRUE;
+    if (hs->defer_close) {
+        hs->deferred_close = TRUE;
         return;
     }
 
     #ifdef MPEXT
     // WinSCP core uses do_select as signalization of connection up/down
-    do_select(ps->plug, INVALID_SOCKET, 0);
+    do_select(hs->plug, INVALID_SOCKET, 0);
     #endif
 
-    handle_free(ps->send_h);
-    handle_free(ps->recv_h);
-    CloseHandle(ps->send_H);
-    if (ps->recv_H != ps->send_H)
-        CloseHandle(ps->recv_H);
+    handle_free(hs->send_h);
+    handle_free(hs->recv_h);
+    CloseHandle(hs->send_H);
+    if (hs->recv_H != hs->send_H)
+        CloseHandle(hs->recv_H);
+    bufchain_clear(&hs->inputdata);
+    bufchain_clear(&hs->stderrdata);
 #ifdef MPEXT
-    if (ps->stderr_h)
+    if (hs->stderr_h)
     {
-        handle_free(ps->stderr_h);
+        handle_free(hs->stderr_h);
     }
-    if (ps->stderr_H)
+    if (hs->stderr_H)
     {
-        CloseHandle(ps->stderr_H);
+        CloseHandle(hs->stderr_H);
     }
 #endif
-    bufchain_clear(&ps->inputdata);
-    bufchain_clear(&ps->stderrdata);
 
-    sfree(ps);
+    sfree(hs);
 }
 
-static int sk_handle_write(Socket s, const char *data, int len)
+static int sk_handle_write(Socket s, const void *data, int len)
 {
-    Handle_Socket ps = (Handle_Socket) s;
+    HandleSocket *hs = FROMFIELD(s, HandleSocket, sockvt);
 
-    return handle_write(ps->send_h, data, len);
+    return handle_write(hs->send_h, data, len);
 }
 
-static int sk_handle_write_oob(Socket s, const char *data, int len)
+static int sk_handle_write_oob(Socket s, const void *data, int len)
 {
     /*
      * oob data is treated as inband; nasty, but nothing really
@@ -176,20 +173,20 @@ static int sk_handle_write_oob(Socket s, const char *data, int len)
 
 static void sk_handle_write_eof(Socket s)
 {
-    Handle_Socket ps = (Handle_Socket) s;
+    HandleSocket *hs = FROMFIELD(s, HandleSocket, sockvt);
 
-    handle_write_eof(ps->send_h);
+    handle_write_eof(hs->send_h);
 }
 
 static void sk_handle_flush(Socket s)
 {
-    /* Handle_Socket ps = (Handle_Socket) s; */
+    /* HandleSocket *hs = FROMFIELD(s, HandleSocket, sockvt); */
     /* do nothing */
 }
 
-static void handle_socket_unfreeze(void *psv)
+static void handle_socket_unfreeze(void *hsv)
 {
-    Handle_Socket ps = (Handle_Socket) psv;
+    HandleSocket *hs = (HandleSocket *)hsv;
     void *data;
     int len;
 
@@ -197,49 +194,49 @@ static void handle_socket_unfreeze(void *psv)
      * If we've been put into a state other than THAWING since the
      * last callback, then we're done.
      */
-    if (ps->frozen != THAWING)
+    if (hs->frozen != THAWING)
         return;
 
     /*
      * Get some of the data we've buffered.
      */
-    bufchain_prefix(&ps->inputdata, &data, &len);
+    bufchain_prefix(&hs->inputdata, &data, &len);
     assert(len > 0);
 
     /*
      * Hand it off to the plug. Be careful of re-entrance - that might
      * have the effect of trying to close this socket.
      */
-    ps->defer_close = TRUE;
-    plug_receive(ps->plug, 0, data, len);
-    bufchain_consume(&ps->inputdata, len);
-    ps->defer_close = FALSE;
-    if (ps->deferred_close) {
-        sk_handle_close(ps);
+    hs->defer_close = TRUE;
+    plug_receive(hs->plug, 0, data, len);
+    bufchain_consume(&hs->inputdata, len);
+    hs->defer_close = FALSE;
+    if (hs->deferred_close) {
+        sk_handle_close(&hs->sockvt);
         return;
     }
 
-    if (bufchain_size(&ps->inputdata) > 0) {
+    if (bufchain_size(&hs->inputdata) > 0) {
         /*
          * If there's still data in our buffer, stay in THAWING state,
          * and reschedule ourself.
          */
-        queue_toplevel_callback(handle_socket_unfreeze, ps);
+        queue_toplevel_callback(handle_socket_unfreeze, hs);
     } else {
         /*
          * Otherwise, we've successfully thawed!
          */
-        ps->frozen = UNFROZEN;
-        handle_unthrottle(ps->recv_h, 0);
+        hs->frozen = UNFROZEN;
+        handle_unthrottle(hs->recv_h, 0);
     }
 }
 
 static void sk_handle_set_frozen(Socket s, int is_frozen)
 {
-    Handle_Socket ps = (Handle_Socket) s;
+    HandleSocket *hs = FROMFIELD(s, HandleSocket, sockvt);
 
     if (is_frozen) {
-        switch (ps->frozen) {
+        switch (hs->frozen) {
           case FREEZING:
           case FROZEN:
             return;                    /* nothing to do */
@@ -251,7 +248,7 @@ static void sk_handle_set_frozen(Socket s, int is_frozen)
              * throttled, so just return to FROZEN state. The toplevel
              * callback will notice and disable itself.
              */
-            ps->frozen = FROZEN;
+            hs->frozen = FROZEN;
             break;
 
           case UNFROZEN:
@@ -259,11 +256,11 @@ static void sk_handle_set_frozen(Socket s, int is_frozen)
              * The normal case. Go to FREEZING, and expect one more
              * load of data from winhandl if we're unlucky.
              */
-            ps->frozen = FREEZING;
+            hs->frozen = FREEZING;
             break;
         }
     } else {
-        switch (ps->frozen) {
+        switch (hs->frozen) {
           case UNFROZEN:
           case THAWING:
             return;                    /* nothing to do */
@@ -274,8 +271,8 @@ static void sk_handle_set_frozen(Socket s, int is_frozen)
              * we were frozen, then we'll still be in this state and
              * can just unfreeze in the trivial way.
              */
-            assert(bufchain_size(&ps->inputdata) == 0);
-            ps->frozen = UNFROZEN;
+            assert(bufchain_size(&hs->inputdata) == 0);
+            hs->frozen = UNFROZEN;
             break;
 
           case FROZEN:
@@ -283,21 +280,21 @@ static void sk_handle_set_frozen(Socket s, int is_frozen)
              * If we have buffered data, go to THAWING and start
              * releasing it in top-level callbacks.
              */
-            ps->frozen = THAWING;
-            queue_toplevel_callback(handle_socket_unfreeze, ps);
+            hs->frozen = THAWING;
+            queue_toplevel_callback(handle_socket_unfreeze, hs);
         }
     }
 }
 
 static const char *sk_handle_socket_error(Socket s)
 {
-    Handle_Socket ps = (Handle_Socket) s;
-    return ps->error;
+    HandleSocket *hs = FROMFIELD(s, HandleSocket, sockvt);
+    return hs->error;
 }
 
 static char *sk_handle_peer_info(Socket s)
 {
-    Handle_Socket ps = (Handle_Socket) s;
+    HandleSocket *hs = FROMFIELD(s, HandleSocket, sockvt);
     ULONG pid;
     static HMODULE kernel32_module;
     DECL_WINDOWS_FUNCTION(static, BOOL, GetNamedPipeClientProcessId,
@@ -324,53 +321,53 @@ static char *sk_handle_peer_info(Socket s)
      * to log what we can find out about the client end.
      */
     if (p_GetNamedPipeClientProcessId &&
-        p_GetNamedPipeClientProcessId(ps->send_H, &pid))
+        p_GetNamedPipeClientProcessId(hs->send_H, &pid))
         return dupprintf("process id %lu", (unsigned long)pid);
 
     return NULL;
 }
 
+static const Socket_vtable HandleSocket_sockvt = {
+    sk_handle_plug,
+    sk_handle_close,
+    sk_handle_write,
+    sk_handle_write_oob,
+    sk_handle_write_eof,
+    sk_handle_flush,
+    sk_handle_set_frozen,
+    sk_handle_socket_error,
+    sk_handle_peer_info,
+};
+
 Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
                           Plug plug, int overlapped)
 {
-    static const struct socket_function_table socket_fn_table = {
-	sk_handle_plug,
-	sk_handle_close,
-	sk_handle_write,
-	sk_handle_write_oob,
-	sk_handle_write_eof,
-	sk_handle_flush,
-	sk_handle_set_frozen,
-	sk_handle_socket_error,
-        sk_handle_peer_info,
-    };
-
-    Handle_Socket ret;
+    HandleSocket *hs;
     int flags = (overlapped ? HANDLE_FLAG_OVERLAPPED : 0);
 
-    ret = snew(struct Socket_handle_tag);
-    ret->fn = &socket_fn_table;
-    ret->plug = plug;
-    ret->error = NULL;
-    ret->frozen = UNFROZEN;
-    bufchain_init(&ret->inputdata);
-    bufchain_init(&ret->stderrdata);
-
-    ret->recv_H = recv_H;
-    ret->recv_h = handle_input_new(ret->recv_H, handle_gotdata, ret, flags);
-    ret->send_H = send_H;
-    ret->send_h = handle_output_new(ret->send_H, handle_sentdata, ret, flags);
-    ret->stderr_H = stderr_H;
-    if (ret->stderr_H)
-        ret->stderr_h = handle_input_new(ret->stderr_H, handle_stderr,
-                                         ret, flags);
-
-    ret->defer_close = ret->deferred_close = FALSE;
+    hs = snew(HandleSocket);
+    hs->sockvt = &HandleSocket_sockvt;
+    hs->plug = plug;
+    hs->error = NULL;
+    hs->frozen = UNFROZEN;
+    bufchain_init(&hs->inputdata);
+    bufchain_init(&hs->stderrdata);
+
+    hs->recv_H = recv_H;
+    hs->recv_h = handle_input_new(hs->recv_H, handle_gotdata, hs, flags);
+    hs->send_H = send_H;
+    hs->send_h = handle_output_new(hs->send_H, handle_sentdata, hs, flags);
+    hs->stderr_H = stderr_H;
+    if (hs->stderr_H)
+        hs->stderr_h = handle_input_new(hs->stderr_H, handle_stderr,
+                                        hs, flags);
+
+    hs->defer_close = hs->deferred_close = FALSE;
 
     #ifdef MPEXT
     // WinSCP core uses do_select as signalization of connection up/down
     do_select(plug, INVALID_SOCKET, 1);
     #endif
 
-    return (Socket) ret;
+    return &hs->sockvt;
 }

+ 84 - 44
source/putty/windows/winmisc.c

@@ -4,6 +4,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <limits.h>
 #include "putty.h"
 #ifndef SECURITY_WIN32
 #define SECURITY_WIN32
@@ -13,7 +14,7 @@
 #include <assert.h>
 #endif
 
-OSVERSIONINFO osVersion;
+DWORD osMajorVersion, osMinorVersion, osPlatformId;
 
 char *platform_get_x_display(void) {
     /* We may as well check for DISPLAY in case it's useful. */
@@ -53,25 +54,13 @@ void filename_free(Filename *fn)
     sfree(fn);
 }
 
-int filename_serialise(const Filename *f, void *vdata)
+void filename_serialise(BinarySink *bs, const Filename *f)
 {
-    char *data = (char *)vdata;
-    int len = strlen(f->path) + 1;     /* include trailing NUL */
-    if (data) {
-        strcpy(data, f->path);
-    }
-    return len;
+    put_asciz(bs, f->path);
 }
-Filename *filename_deserialise(void *vdata, int maxsize, int *used)
+Filename *filename_deserialise(BinarySource *src)
 {
-    char *data = (char *)vdata;
-    char *end;
-    end = memchr(data, '\0', maxsize);
-    if (!end)
-        return NULL;
-    end++;
-    *used = end - data;
-    return filename_from_str(data);
+    return filename_from_str(get_asciz(src));
 }
 
 char filename_char_sanitise(char c)
@@ -143,6 +132,7 @@ char *get_username(void)
 	       sspicli.dll WITHOUT proper path sanitizing, so better
 	       load it properly before */
 	    HMODULE sspicli = load_system32_dll("sspicli.dll");
+            (void)sspicli; /* squash compiler warning about unused variable */
 	    GET_WINDOWS_FUNCTION(secur32, GetUserNameExA);
 	    tried_usernameex = TRUE;
 	}
@@ -234,11 +224,41 @@ void dll_hijacking_protection(void)
     }
 }
 
-BOOL init_winver(void)
+void init_winver(void)
 {
+    OSVERSIONINFO osVersion;
+    static HMODULE kernel32_module;
+    DECL_WINDOWS_FUNCTION(static, BOOL, GetVersionExA, (LPOSVERSIONINFO));
+
+    if (!kernel32_module) {
+        kernel32_module = load_system32_dll("kernel32.dll");
+        /* Deliberately don't type-check this function, because that
+         * would involve using its declaration in a header file which
+         * triggers a deprecation warning. I know it's deprecated (see
+         * below) and don't need telling. */
+        GET_WINDOWS_FUNCTION_NO_TYPECHECK(kernel32_module, GetVersionExA);
+    }
+
     ZeroMemory(&osVersion, sizeof(osVersion));
     osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
-    return GetVersionEx ( (OSVERSIONINFO *) &osVersion);
+    if (p_GetVersionExA && p_GetVersionExA(&osVersion)) {
+        osMajorVersion = osVersion.dwMajorVersion;
+        osMinorVersion = osVersion.dwMinorVersion;
+        osPlatformId = osVersion.dwPlatformId;
+    } else {
+        /*
+         * GetVersionEx is deprecated, so allow for it perhaps going
+         * away in future API versions. If it's not there, simply
+         * assume that's because Windows is too _new_, so fill in the
+         * variables we care about to a value that will always compare
+         * higher than any given test threshold.
+         *
+         * Normally we should be checking against the presence of a
+         * specific function if possible in any case.
+         */
+        osMajorVersion = osMinorVersion = UINT_MAX; /* a very high number */
+        osPlatformId = VER_PLATFORM_WIN32_NT; /* not Win32s or Win95-like */
+    }
 }
 
 #ifdef MPEXT
@@ -606,31 +626,51 @@ void fontspec_free(FontSpec *f)
     sfree(f->name);
     sfree(f);
 }
-int fontspec_serialise(FontSpec *f, void *vdata)
+void fontspec_serialise(BinarySink *bs, FontSpec *f)
 {
-    char *data = (char *)vdata;
-    int len = strlen(f->name) + 1;     /* include trailing NUL */
-    if (data) {
-        strcpy(data, f->name);
-        PUT_32BIT_MSB_FIRST(data + len, f->isbold);
-        PUT_32BIT_MSB_FIRST(data + len + 4, f->height);
-        PUT_32BIT_MSB_FIRST(data + len + 8, f->charset);
+    put_asciz(bs, f->name);
+    put_uint32(bs, f->isbold);
+    put_uint32(bs, f->height);
+    put_uint32(bs, f->charset);
+}
+FontSpec *fontspec_deserialise(BinarySource *src)
+{
+    const char *name = get_asciz(src);
+    unsigned isbold = get_uint32(src);
+    unsigned height = get_uint32(src);
+    unsigned charset = get_uint32(src);
+    return fontspec_new(name, isbold, height, charset);
+}
+
+int open_for_write_would_lose_data(const Filename *fn)
+{
+    WIN32_FILE_ATTRIBUTE_DATA attrs;
+    if (!GetFileAttributesEx(fn->path, GetFileExInfoStandard, &attrs)) {
+        /*
+         * Generally, if we don't identify a specific reason why we
+         * should return true from this function, we return false, and
+         * let the subsequent attempt to open the file for real give a
+         * more useful error message.
+         */
+        return FALSE;
+    }
+    if (attrs.dwFileAttributes & (FILE_ATTRIBUTE_DEVICE |
+                                  FILE_ATTRIBUTE_DIRECTORY)) {
+        /*
+         * File is something other than an ordinary disk file, so
+         * opening it for writing will not cause truncation. (It may
+         * not _succeed_ either, but that's not our problem here!)
+         */
+        return FALSE;
+    }
+    if (attrs.nFileSizeHigh == 0 && attrs.nFileSizeLow == 0) {
+        /*
+         * File is zero-length (or may be a named pipe, which
+         * dwFileAttributes can't tell apart from a regular file), so
+         * opening it for writing won't truncate any data away because
+         * there's nothing to truncate anyway.
+         */
+        return FALSE;
     }
-    return len + 12;                   /* also include three 4-byte ints */
-}
-FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used)
-{
-    char *data = (char *)vdata;
-    char *end;
-    if (maxsize < 13)
-        return NULL;
-    end = memchr(data, '\0', maxsize-12);
-    if (!end)
-        return NULL;
-    end++;
-    *used = end - data + 12;
-    return fontspec_new(data,
-                        GET_32BIT_MSB_FIRST(end),
-                        GET_32BIT_MSB_FIRST(end + 4),
-                        GET_32BIT_MSB_FIRST(end + 8));
+    return TRUE;
 }

+ 106 - 145
source/putty/windows/winnet.c

@@ -39,16 +39,6 @@ const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
 #define ipv4_is_loopback(addr) \
 	((p_ntohl(addr.s_addr) & 0xFF000000L) == 0x7F000000L)
 
-/*
- * We used to typedef struct Socket_tag *Socket.
- *
- * Since we have made the networking abstraction slightly more
- * abstract, Socket no longer means a tcp socket (it could mean
- * an ssl socket).  So now we must use Actual_Socket when we know
- * we are talking about a tcp socket.
- */
-typedef struct Socket_tag *Actual_Socket;
-
 /*
  * Mutable state that goes with a SockAddr: stores information
  * about where in the list of candidate IP(v*) addresses we've
@@ -62,9 +52,8 @@ struct SockAddrStep_tag {
     int curraddr;
 };
 
-struct Socket_tag {
-    const struct socket_function_table *fn;
-    /* the above variable absolutely *must* be the first in this structure */
+typedef struct NetSocket NetSocket;
+struct NetSocket {
     const char *error;
     SOCKET s;
     Plug plug;
@@ -89,7 +78,9 @@ struct Socket_tag {
      * example. So here we define `parent' and `child' pointers to
      * track this link.
      */
-    Actual_Socket parent, child;
+    NetSocket *parent, *child;
+
+    const Socket_vtable *sockvt;
 };
 
 struct SockAddr_tag {
@@ -137,7 +128,7 @@ static tree234 *sktree;
 
 static int cmpfortree(void *av, void *bv)
 {
-    Actual_Socket a = (Actual_Socket) av, b = (Actual_Socket) bv;
+    NetSocket *a = (NetSocket *)av, *b = (NetSocket *)bv;
     unsigned long as = (unsigned long) a->s, bs = (unsigned long) b->s;
     if (as < bs)
 	return -1;
@@ -152,7 +143,7 @@ static int cmpfortree(void *av, void *bv)
 
 static int cmpforsearch(void *av, void *bv)
 {
-    Actual_Socket b = (Actual_Socket) bv;
+    NetSocket *b = (NetSocket *)bv;
     uintptr_t as = (uintptr_t) av, bs = (uintptr_t) b->s;
     if (as < bs)
 	return -1;
@@ -209,8 +200,8 @@ DECL_WINDOWS_FUNCTION(static, int, getaddrinfo,
 DECL_WINDOWS_FUNCTION(static, void, freeaddrinfo, (struct addrinfo *res));
 DECL_WINDOWS_FUNCTION(static, int, getnameinfo,
 		      (const struct sockaddr FAR * sa, socklen_t salen,
-		       char FAR * host, size_t hostlen, char FAR * serv,
-		       size_t servlen, int flags));
+		       char FAR * host, DWORD hostlen, char FAR * serv,
+		       DWORD servlen, int flags));
 DECL_WINDOWS_FUNCTION(static, char *, gai_strerror, (int ecode));
 DECL_WINDOWS_FUNCTION(static, int, WSAAddressToStringA,
 		      (LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFO,
@@ -266,7 +257,8 @@ void sk_init(void)
     }
     if (!winsock_module)
     {
-	fatalbox("Unable to load any WinSock library");
+	modalfatalbox("Unable to load any WinSock library");
+
     }
 
 #ifndef NO_IPV6
@@ -313,7 +305,11 @@ void sk_init(void)
 
     GET_WINDOWS_FUNCTION(winsock_module, WSAAsyncSelect);
     GET_WINDOWS_FUNCTION(winsock_module, WSAEventSelect);
-    GET_WINDOWS_FUNCTION(winsock_module, select);
+    /* We don't type-check select because at least some MinGW versions
+     * of the Windows API headers seem to disagree with the
+     * documentation on whether the 'struct timeval *' pointer is
+     * const or not. */
+    GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, select);
     GET_WINDOWS_FUNCTION(winsock_module, WSAGetLastError);
     GET_WINDOWS_FUNCTION(winsock_module, WSAEnumNetworkEvents);
     GET_WINDOWS_FUNCTION(winsock_module, WSAStartup);
@@ -324,7 +320,7 @@ void sk_init(void)
     GET_WINDOWS_FUNCTION(winsock_module, htonl);
     GET_WINDOWS_FUNCTION(winsock_module, htons);
     GET_WINDOWS_FUNCTION(winsock_module, ntohs);
-    GET_WINDOWS_FUNCTION(winsock_module, gethostname);
+    GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, gethostname);
 #else
     /* The toolchain I use for Windows Coverity builds doesn't know
      * the type signatures of these */
@@ -365,7 +361,7 @@ void sk_init(void)
     if (!sk_startup(2,2) &&
 	!sk_startup(2,0) &&
 	!sk_startup(1,1)) {
-	fatalbox("Unable to initialise WinSock");
+	modalfatalbox("Unable to initialise WinSock");
     }
 
     sktree = newtree234(cmpfortree);
@@ -373,7 +369,7 @@ void sk_init(void)
 
 void sk_cleanup(void)
 {
-    Actual_Socket s;
+    NetSocket *s;
     int i;
 
     if (sktree) {
@@ -936,16 +932,16 @@ SockAddr sk_addr_dup(SockAddr addr)
     return addr;
 }
 
-static Plug sk_tcp_plug(Socket sock, Plug p)
+static Plug sk_net_plug(Socket sock, Plug p)
 {
-    Actual_Socket s = (Actual_Socket) sock;
+    NetSocket *s = FROMFIELD(sock, NetSocket, sockvt);
     Plug ret = s->plug;
     if (p)
 	s->plug = p;
     return ret;
 }
 
-static void sk_tcp_flush(Socket s)
+static void sk_net_flush(Socket s)
 {
     /*
      * We send data to the socket as soon as we can anyway,
@@ -953,13 +949,13 @@ static void sk_tcp_flush(Socket s)
      */
 }
 
-static void sk_tcp_close(Socket s);
-static int sk_tcp_write(Socket s, const char *data, int len);
-static int sk_tcp_write_oob(Socket s, const char *data, int len);
-static void sk_tcp_write_eof(Socket s);
-static void sk_tcp_set_frozen(Socket s, int is_frozen);
-static const char *sk_tcp_socket_error(Socket s);
-static char *sk_tcp_peer_info(Socket s);
+static void sk_net_close(Socket s);
+static int sk_net_write(Socket s, const void *data, int len);
+static int sk_net_write_oob(Socket s, const void *data, int len);
+static void sk_net_write_eof(Socket s);
+static void sk_net_set_frozen(Socket s, int is_frozen);
+static const char *sk_net_socket_error(Socket s);
+static char *sk_net_peer_info(Socket s);
 
 #ifdef MPEXT
 extern char *do_select(Plug plug, SOCKET skt, int startup);
@@ -967,29 +963,29 @@ extern char *do_select(Plug plug, SOCKET skt, int startup);
 extern char *do_select(SOCKET skt, int startup);
 #endif
 
-static Socket sk_tcp_accept(accept_ctx_t ctx, Plug plug)
-{
-    static const struct socket_function_table fn_table = {
-	sk_tcp_plug,
-	sk_tcp_close,
-	sk_tcp_write,
-	sk_tcp_write_oob,
-	sk_tcp_write_eof,
-	sk_tcp_flush,
-	sk_tcp_set_frozen,
-	sk_tcp_socket_error,
-	sk_tcp_peer_info,
-    };
+static const Socket_vtable NetSocket_sockvt = {
+    sk_net_plug,
+    sk_net_close,
+    sk_net_write,
+    sk_net_write_oob,
+    sk_net_write_eof,
+    sk_net_flush,
+    sk_net_set_frozen,
+    sk_net_socket_error,
+    sk_net_peer_info,
+};
 
+static Socket sk_net_accept(accept_ctx_t ctx, Plug plug)
+{
     DWORD err;
     char *errstr;
-    Actual_Socket ret;
+    NetSocket *ret;
 
     /*
-     * Create Socket structure.
+     * Create NetSocket structure.
      */
-    ret = snew(struct Socket_tag);
-    ret->fn = &fn_table;
+    ret = snew(NetSocket);
+    ret->sockvt = &NetSocket_sockvt;
     ret->error = NULL;
     ret->plug = plug;
     bufchain_init(&ret->output_data);
@@ -1008,7 +1004,7 @@ static Socket sk_tcp_accept(accept_ctx_t ctx, Plug plug)
     if (ret->s == INVALID_SOCKET) {
 	err = p_WSAGetLastError();
 	ret->error = winsock_error_string(err);
-	return (Socket) ret;
+	return &ret->sockvt;
     }
 
     ret->oobinline = 0;
@@ -1022,15 +1018,15 @@ static Socket sk_tcp_accept(accept_ctx_t ctx, Plug plug)
 #endif
     if (errstr) {
 	ret->error = errstr;
-	return (Socket) ret;
+	return &ret->sockvt;
     }
 
     add234(sktree, ret);
 
-    return (Socket) ret;
+    return &ret->sockvt;
 }
 
-static DWORD try_connect(Actual_Socket sock,
+static DWORD try_connect(NetSocket *sock,
 #ifdef MPEXT
                          int timeout,
                          int sndbuf
@@ -1298,26 +1294,14 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
 #endif
 	      )
 {
-    static const struct socket_function_table fn_table = {
-	sk_tcp_plug,
-	sk_tcp_close,
-	sk_tcp_write,
-	sk_tcp_write_oob,
-	sk_tcp_write_eof,
-	sk_tcp_flush,
-	sk_tcp_set_frozen,
-	sk_tcp_socket_error,
-	sk_tcp_peer_info,
-    };
-
-    Actual_Socket ret;
+    NetSocket *ret;
     DWORD err;
 
     /*
-     * Create Socket structure.
+     * Create NetSocket structure.
      */
-    ret = snew(struct Socket_tag);
-    ret->fn = &fn_table;
+    ret = snew(NetSocket);
+    ret->sockvt = &NetSocket_sockvt;
     ret->error = NULL;
     ret->plug = plug;
     bufchain_init(&ret->output_data);
@@ -1351,24 +1335,12 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
         );
     } while (err && sk_nextaddr(ret->addr, &ret->step));
 
-    return (Socket) ret;
+    return &ret->sockvt;
 }
 
 Socket sk_newlistener(const char *srcaddr, int port, Plug plug,
                       int local_host_only, int orig_address_family)
 {
-    static const struct socket_function_table fn_table = {
-	sk_tcp_plug,
-	sk_tcp_close,
-	sk_tcp_write,
-	sk_tcp_write_oob,
-	sk_tcp_write_eof,
-	sk_tcp_flush,
-	sk_tcp_set_frozen,
-	sk_tcp_socket_error,
-	sk_tcp_peer_info,
-    };
-
     SOCKET s;
 #ifndef NO_IPV6
     SOCKADDR_IN6 a6;
@@ -1377,17 +1349,17 @@ Socket sk_newlistener(const char *srcaddr, int port, Plug plug,
 
     DWORD err;
     char *errstr;
-    Actual_Socket ret;
+    NetSocket *ret;
     int retcode;
     int on = 1;
 
     int address_family;
 
     /*
-     * Create Socket structure.
+     * Create NetSocket structure.
      */
-    ret = snew(struct Socket_tag);
-    ret->fn = &fn_table;
+    ret = snew(NetSocket);
+    ret->sockvt = &NetSocket_sockvt;
     ret->error = NULL;
     ret->plug = plug;
     bufchain_init(&ret->output_data);
@@ -1429,10 +1401,10 @@ Socket sk_newlistener(const char *srcaddr, int port, Plug plug,
     if (s == INVALID_SOCKET) {
 	err = p_WSAGetLastError();
 	ret->error = winsock_error_string(err);
-	return (Socket) ret;
+	return &ret->sockvt;
     }
 
-	SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0);
+    SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0);
 
     ret->oobinline = 0;
 
@@ -1515,14 +1487,14 @@ Socket sk_newlistener(const char *srcaddr, int port, Plug plug,
     if (err) {
 	p_closesocket(s);
 	ret->error = winsock_error_string(err);
-	return (Socket) ret;
+	return &ret->sockvt;
     }
 
 
     if (p_listen(s, SOMAXCONN) == SOCKET_ERROR) {
         p_closesocket(s);
 	ret->error = winsock_error_string(p_WSAGetLastError());
-	return (Socket) ret;
+	return &ret->sockvt;
     }
 
     /* Set up a select mechanism. This could be an AsyncSelect on a
@@ -1535,7 +1507,7 @@ Socket sk_newlistener(const char *srcaddr, int port, Plug plug,
     if (errstr) {
 	p_closesocket(s);
 	ret->error = errstr;
-	return (Socket) ret;
+	return &ret->sockvt;
     }
 
     add234(sktree, ret);
@@ -1546,36 +1518,35 @@ Socket sk_newlistener(const char *srcaddr, int port, Plug plug,
      * IPv6 listening socket and link it to this one.
      */
     if (address_family == AF_INET && orig_address_family == ADDRTYPE_UNSPEC) {
-	Actual_Socket other;
-
-	other = (Actual_Socket) sk_newlistener(srcaddr, port, plug,
-					       local_host_only, ADDRTYPE_IPV6);
+	Socket other = sk_newlistener(srcaddr, port, plug,
+                                      local_host_only, ADDRTYPE_IPV6);
 
 	if (other) {
-	    if (!other->error) {
-		other->parent = ret;
-		ret->child = other;
+            NetSocket *ns = FROMFIELD(other, NetSocket, sockvt);
+	    if (!ns->error) {
+		ns->parent = ret;
+		ret->child = ns;
 	    } else {
-		sfree(other);
+		sfree(ns);
 	    }
 	}
     }
 #endif
 
-    return (Socket) ret;
+    return &ret->sockvt;
 }
 
-static void sk_tcp_close(Socket sock)
+static void sk_net_close(Socket sock)
 {
 #ifdef MPEXT
     extern char *do_select(Plug plug, SOCKET skt, int startup);
 #else
     extern char *do_select(SOCKET skt, int startup);
 #endif
-    Actual_Socket s = (Actual_Socket) sock;
+    NetSocket *s = FROMFIELD(sock, NetSocket, sockvt);
 
     if (s->child)
-	sk_tcp_close((Socket)s->child);
+	sk_net_close(&s->child->sockvt);
 
     del234(sktree, s);
 #ifdef MPEXT
@@ -1594,7 +1565,7 @@ static void sk_tcp_close(Socket sock)
  */
 static void socket_error_callback(void *vs)
 {
-    Actual_Socket s = (Actual_Socket)vs;
+    NetSocket *s = (NetSocket *)vs;
 
     /*
      * Just in case other socket work has caused this socket to vanish
@@ -1614,7 +1585,7 @@ static void socket_error_callback(void *vs)
  * The function which tries to send on a socket once it's deemed
  * writable.
  */
-void try_send(Actual_Socket s)
+void try_send(NetSocket *s)
 {
     while (s->sending_oob || bufchain_size(&s->output_data) > 0) {
 	int nsent;
@@ -1646,26 +1617,20 @@ void try_send(Actual_Socket s)
 		 */
 		s->writable = FALSE;
 		return;
-	    } else if (nsent == 0 ||
-		       err == WSAECONNABORTED || err == WSAECONNRESET) {
+	    } else {
 		/*
-		 * If send() returns CONNABORTED or CONNRESET, we
-		 * unfortunately can't just call plug_closing(),
-		 * because it's quite likely that we're currently
-		 * _in_ a call from the code we'd be calling back
-		 * to, so we'd have to make half the SSH code
-		 * reentrant. Instead we flag a pending error on
-		 * the socket, to be dealt with (by calling
-		 * plug_closing()) at some suitable future moment.
+		 * If send() returns a socket error, we unfortunately
+		 * can't just call plug_closing(), because it's quite
+		 * likely that we're currently _in_ a call from the
+		 * code we'd be calling back to, so we'd have to make
+		 * half the SSH code reentrant. Instead we flag a
+		 * pending error on the socket, to be dealt with (by
+		 * calling plug_closing()) at some suitable future
+		 * moment.
 		 */
 		s->pending_error = err;
                 queue_toplevel_callback(socket_error_callback, s);
 		return;
-	    } else {
-		/* We're inside the Windows frontend here, so we know
-		 * that the frontend handle is unnecessary. */
-		logevent(NULL, winsock_error_string(err));
-		fatalbox("%s", winsock_error_string(err));
 	    }
 	} else {
 	    if (s->sending_oob) {
@@ -1691,9 +1656,9 @@ void try_send(Actual_Socket s)
     }
 }
 
-static int sk_tcp_write(Socket sock, const char *buf, int len)
+static int sk_net_write(Socket sock, const void *buf, int len)
 {
-    Actual_Socket s = (Actual_Socket) sock;
+    NetSocket *s = FROMFIELD(sock, NetSocket, sockvt);
 
     assert(s->outgoingeof == EOF_NO);
 
@@ -1711,9 +1676,9 @@ static int sk_tcp_write(Socket sock, const char *buf, int len)
     return bufchain_size(&s->output_data);
 }
 
-static int sk_tcp_write_oob(Socket sock, const char *buf, int len)
+static int sk_net_write_oob(Socket sock, const void *buf, int len)
 {
-    Actual_Socket s = (Actual_Socket) sock;
+    NetSocket *s = FROMFIELD(sock, NetSocket, sockvt);
 
     assert(s->outgoingeof == EOF_NO);
 
@@ -1734,9 +1699,9 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len)
     return s->sending_oob;
 }
 
-static void sk_tcp_write_eof(Socket sock)
+static void sk_net_write_eof(Socket sock)
 {
-    Actual_Socket s = (Actual_Socket) sock;
+    NetSocket *s = FROMFIELD(sock, NetSocket, sockvt);
 
     assert(s->outgoingeof == EOF_NO);
 
@@ -1757,7 +1722,7 @@ void select_result(WPARAM wParam, LPARAM lParam)
     int ret;
     DWORD err;
     char buf[20480];		       /* nice big buffer for plenty of speed */
-    Actual_Socket s;
+    NetSocket *s;
     u_long atmark;
 
     /* wParam is the socket itself */
@@ -1861,12 +1826,8 @@ void select_result(WPARAM wParam, LPARAM lParam)
 	ret = p_recv(s->s, buf, sizeof(buf), MSG_OOB);
 	noise_ultralight(ret);
 	if (ret <= 0) {
-	    const char *str = (ret == 0 ? "Internal networking trouble" :
-			 winsock_error_string(p_WSAGetLastError()));
-	    /* We're inside the Windows frontend here, so we know
-	     * that the frontend handle is unnecessary. */
-	    logevent(NULL, str);
-	    fatalbox("%s", str);
+            int err = p_WSAGetLastError();
+	    plug_closing(s->plug, winsock_error_string(err), err, 0);
 	} else {
 	    plug_receive(s->plug, 2, buf, ret);
 	}
@@ -1933,7 +1894,7 @@ void select_result(WPARAM wParam, LPARAM lParam)
 #endif
 	    {
 		p_closesocket(t);      /* dodgy WinSock let nonlocal through */
-	    } else if (plug_accepting(s->plug, sk_tcp_accept, actx)) {
+	    } else if (plug_accepting(s->plug, sk_net_accept, actx)) {
 		p_closesocket(t);      /* denied or error */
 	    }
 	}
@@ -1949,15 +1910,15 @@ const char *sk_addr_error(SockAddr addr)
 {
     return addr->error;
 }
-static const char *sk_tcp_socket_error(Socket sock)
+static const char *sk_net_socket_error(Socket sock)
 {
-    Actual_Socket s = (Actual_Socket) sock;
+    NetSocket *s = FROMFIELD(sock, NetSocket, sockvt);
     return s->error;
 }
 
-static char *sk_tcp_peer_info(Socket sock)
+static char *sk_net_peer_info(Socket sock)
 {
-    Actual_Socket s = (Actual_Socket) sock;
+    NetSocket *s = FROMFIELD(sock, NetSocket, sockvt);
 #ifdef NO_IPV6
     struct sockaddr_in addr;
 #else
@@ -1987,9 +1948,9 @@ static char *sk_tcp_peer_info(Socket sock)
     }
 }
 
-static void sk_tcp_set_frozen(Socket sock, int is_frozen)
+static void sk_net_set_frozen(Socket sock, int is_frozen)
 {
-    Actual_Socket s = (Actual_Socket) sock;
+    NetSocket *s = FROMFIELD(sock, NetSocket, sockvt);
     if (s->frozen == is_frozen)
 	return;
     s->frozen = is_frozen;
@@ -2009,7 +1970,7 @@ static void sk_tcp_set_frozen(Socket sock, int is_frozen)
 
 void socket_reselect_all(void)
 {
-    Actual_Socket s;
+    NetSocket *s;
     int i;
 
     for (i = 0; (s = index234(sktree, i)) != NULL; i++) {
@@ -2027,7 +1988,7 @@ void socket_reselect_all(void)
  */
 SOCKET first_socket(int *state)
 {
-    Actual_Socket s;
+    NetSocket *s;
     *state = 0;
     s = index234(sktree, (*state)++);
     return s ? s->s : INVALID_SOCKET;
@@ -2035,13 +1996,13 @@ SOCKET first_socket(int *state)
 
 SOCKET next_socket(int *state)
 {
-    Actual_Socket s = index234(sktree, (*state)++);
+    NetSocket *s = index234(sktree, (*state)++);
     return s ? s->s : INVALID_SOCKET;
 }
 
 extern int socket_writable(SOCKET skt)
 {
-    Actual_Socket s = find234(sktree, (void *)skt, cmpforsearch);
+    NetSocket *s = find234(sktree, (void *)skt, cmpforsearch);
 
     if (s)
 	return bufchain_size(&s->output_data) > 0;

+ 27 - 17
source/putty/windows/winnoise.c

@@ -18,6 +18,29 @@ DECL_WINDOWS_FUNCTION(static, BOOL, CryptReleaseContext,
                       (HCRYPTPROV, DWORD));
 static HMODULE wincrypt_module = NULL;
 
+int win_read_random(void *buf, unsigned wanted)
+{
+    int toret = FALSE;
+    HCRYPTPROV crypt_provider;
+
+    if (!wincrypt_module) {
+        wincrypt_module = load_system32_dll("advapi32.dll");
+        GET_WINDOWS_FUNCTION(wincrypt_module, CryptAcquireContextA);
+        GET_WINDOWS_FUNCTION(wincrypt_module, CryptGenRandom);
+        GET_WINDOWS_FUNCTION(wincrypt_module, CryptReleaseContext);
+    }
+
+    if (wincrypt_module && p_CryptAcquireContextA &&
+        p_CryptGenRandom && p_CryptReleaseContext &&
+        p_CryptAcquireContextA(&crypt_provider, NULL, NULL, PROV_RSA_FULL,
+                               CRYPT_VERIFYCONTEXT)) {
+        toret = p_CryptGenRandom(crypt_provider, wanted, buf);
+        p_CryptReleaseContext(crypt_provider, 0);
+    }
+
+    return toret;
+}
+
 /*
  * This function is called once, at PuTTY startup.
  */
@@ -27,8 +50,8 @@ void noise_get_heavy(void (*func) (void *, int))
     HANDLE srch;
     WIN32_FIND_DATA finddata;
     DWORD pid;
-    HCRYPTPROV crypt_provider;
     char winpath[MAX_PATH + 3];
+    BYTE buf[32];
 
     GetWindowsDirectory(winpath, sizeof(winpath));
     strcat(winpath, "\\*");
@@ -43,22 +66,9 @@ void noise_get_heavy(void (*func) (void *, int))
     pid = GetCurrentProcessId();
     func(&pid, sizeof(pid));
 
-    if (!wincrypt_module) {
-        wincrypt_module = load_system32_dll("advapi32.dll");
-        GET_WINDOWS_FUNCTION(wincrypt_module, CryptAcquireContextA);
-        GET_WINDOWS_FUNCTION(wincrypt_module, CryptGenRandom);
-        GET_WINDOWS_FUNCTION(wincrypt_module, CryptReleaseContext);
-    }
-
-    if (wincrypt_module && p_CryptAcquireContextA &&
-        p_CryptGenRandom && p_CryptReleaseContext &&
-        p_CryptAcquireContextA(&crypt_provider, NULL, NULL, PROV_RSA_FULL,
-                               CRYPT_VERIFYCONTEXT)) {
-        BYTE buf[32];
-        if (p_CryptGenRandom(crypt_provider, 32, buf)) {
-            func(buf, sizeof(buf));
-        }
-        p_CryptReleaseContext(crypt_provider, 0);
+    if (win_read_random(buf, sizeof(buf))) {
+        func(buf, sizeof(buf));
+        smemclr(buf, sizeof(buf));
     }
 
     read_random_seed(func);

+ 6 - 2
source/putty/windows/winpgntc.c

@@ -31,7 +31,7 @@ void agent_cancel_query(agent_pending_query *q)
 }
 
 agent_pending_query *agent_query(
-    void *in, int inlen, void **out, int *outlen,
+    strbuf *query, void **out, int *outlen,
     void (*callback)(void *, void *, int), void *callback_ctx)
 {
     HWND hwnd;
@@ -47,6 +47,9 @@ agent_pending_query *agent_query(
     *out = NULL;
     *outlen = 0;
 
+    if (query->len > AGENT_MAX_MSGLEN)
+        return NULL;                   /* query too large */
+
     hwnd = FindWindow("Pageant", "Pageant");
     if (!hwnd)
 	return NULL;		       /* *out == NULL, so failure */
@@ -93,7 +96,8 @@ agent_pending_query *agent_query(
 	return NULL;		       /* *out == NULL, so failure */
     }
     p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
-    memcpy(p, in, inlen);
+    strbuf_finalise_agent_query(query);
+    memcpy(p, query->s, query->len);
     cds.dwData = AGENT_COPYDATA_ID;
     cds.cbData = 1 + strlen(mapname);
     cds.lpData = mapname;

+ 37 - 14
source/putty/windows/winstuff.h

@@ -23,13 +23,21 @@
 #include <stdint.h>
 #endif
 
+#include "defs.h"
+
 #include "tree234.h"
 
 #ifndef MPEXT
 #include "winhelp.h"
 #endif
 
+#if defined _M_IX86 || defined _M_AMD64
+#define BUILDINFO_PLATFORM "x86 Windows"
+#elif defined _M_ARM || defined _M_ARM64
+#define BUILDINFO_PLATFORM "Arm Windows"
+#else
 #define BUILDINFO_PLATFORM "Windows"
+#endif
 
 struct Filename {
     char *path;
@@ -62,6 +70,10 @@ struct FontSpec *fontspec_new(const char *name,
 #define PLATFORM_IS_UTF16 /* enable UTF-16 processing when exchanging
 			   * wchar_t strings with environment */
 
+#define PLATFORM_CLIPBOARDS(X)                      \
+    X(CLIP_SYSTEM, "system clipboard")              \
+    /* end of list */
+
 /*
  * Where we can, we use GetWindowLongPtr and friends because they're
  * more useful on 64-bit platforms, but they're a relatively recent
@@ -139,8 +151,6 @@ struct FontSpec *fontspec_new(const char *name,
  *
  * (DECL_WINDOWS_FUNCTION works with both these variants.)
  */
-#define TYPECHECK(to_check, to_return)          \
-    (sizeof(to_check) ? to_return : to_return)
 #define DECL_WINDOWS_FUNCTION(linkage, rettype, name, params)   \
     typedef rettype (WINAPI *t_##name) params;                  \
     linkage t_##name p_##name
@@ -171,13 +181,6 @@ struct FontSpec *fontspec_new(const char *name,
 #endif
 #endif
 
-#ifndef DONE_TYPEDEFS
-#define DONE_TYPEDEFS
-typedef struct conf_tag Conf;
-typedef struct backend_tag Backend;
-typedef struct terminal_tag Terminal;
-#endif
-
 #define PUTTY_REG_POS "Software\\SimonTatham\\PuTTY"
 #define PUTTY_REG_PARENT "Software\\SimonTatham"
 #define PUTTY_REG_PARENT_CHILD "PuTTY"
@@ -205,9 +208,6 @@ typedef struct terminal_tag Terminal;
 
 typedef HDC Context;
 
-typedef unsigned int uint32; /* int is 32-bits on Win32 and Win64. */
-#define PUTTY_UINT32_DEFINED
-
 #ifndef NO_GSSAPI
 /*
  * GSS-API stuff
@@ -253,6 +253,13 @@ void quit_help(HWND hwnd);
 GLOBAL Terminal *term;
 GLOBAL void *logctx;
 
+/*
+ * Windows-specific clipboard helper function shared with windlg.c,
+ * which takes the data string in the system code page instead of
+ * Unicode.
+ */
+void write_aclip(void *frontend, int clipboard, char *, int, int);
+
 #define WM_NETEVENT  (WM_APP + 5)
 
 /*
@@ -525,9 +532,9 @@ void show_help(HWND hwnd);
 /*
  * Exports from winmisc.c.
  */
-extern OSVERSIONINFO osVersion;
+GLOBAL DWORD osMajorVersion, osMinorVersion, osPlatformId;
+void init_winver(void);
 void dll_hijacking_protection(void);
-BOOL init_winver(void);
 HMODULE load_system32_dll(const char *libname);
 const char *win_strerror(int error);
 void restrict_process_acl(void);
@@ -615,6 +622,11 @@ void remove_session_from_jumplist(const char * const sessionname);
 void clear_jumplist(void);
 BOOL set_explicit_app_user_model_id();
 
+/*
+ * Exports from winnoise.c.
+ */
+int win_read_random(void *buf, unsigned wanted); /* returns TRUE on success */
+
 /*
  * Extra functions in winstore.c over and above the interface in
  * storage.h.
@@ -638,4 +650,15 @@ int remove_from_jumplist_registry(const char *item);
  * empty one. */
 char *get_jumplist_registry_entries(void);
 
+/*
+ * Windows clipboard-UI wording.
+ */
+#define CLIPNAME_IMPLICIT "Last selected text"
+#define CLIPNAME_EXPLICIT "System clipboard"
+#define CLIPNAME_EXPLICIT_OBJECT "system clipboard"
+/* These defaults are the ones PuTTY has historically had */
+#define CLIPUI_DEFAULT_AUTOCOPY TRUE
+#define CLIPUI_DEFAULT_MOUSE CLIPUI_EXPLICIT
+#define CLIPUI_DEFAULT_INS CLIPUI_EXPLICIT
+
 #endif

+ 101 - 83
source/putty/x11fwd.c

@@ -27,8 +27,6 @@ struct XDMSeen {
 };
 
 struct X11Connection {
-    const struct plug_function_table *fn;
-    /* the above variable absolutely *must* be the first in this structure */
     unsigned char firstpkt[12];	       /* first X data packet */
     tree234 *authtree;
     struct X11Display *disp;
@@ -42,6 +40,8 @@ struct X11Connection {
     int peer_port;
     struct ssh_channel *c;        /* channel structure held by ssh.c */
     Socket s;
+
+    const Plug_vtable *plugvt;
 };
 
 static int xdmseen_cmp(void *a, void *b)
@@ -52,22 +52,6 @@ static int xdmseen_cmp(void *a, void *b)
            memcmp(sa->clientid, sb->clientid, sizeof(sa->clientid));
 }
 
-/* Do-nothing "plug" implementation, used by x11_setup_display() when it
- * creates a trial connection (and then immediately closes it).
- * XXX: bit out of place here, could in principle live in a platform-
- *      independent network.c or something */
-static void dummy_plug_log(Plug p, int type, SockAddr addr, int port,
-			   const char *error_msg, int error_code) { }
-static void dummy_plug_closing
-     (Plug p, const char *error_msg, int error_code, int calling_back) { }
-static void dummy_plug_receive(Plug p, int urgent, char *data, int len) { }
-static void dummy_plug_sent(Plug p, int bufsize) { }
-static int dummy_plug_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx) { return 1; }
-static const struct plug_function_table dummy_plug = {
-    dummy_plug_log, dummy_plug_closing, dummy_plug_receive,
-    dummy_plug_sent, dummy_plug_accepting
-};
-
 struct X11FakeAuth *x11_invent_fake_auth(tree234 *authtree, int authtype)
 {
     struct X11FakeAuth *auth = snew(struct X11FakeAuth);
@@ -306,8 +290,7 @@ struct X11Display *x11_setup_display(const char *display, Conf *conf)
 	if (!err) {
 	    /* Create trial connection to see if there is a useful Unix-domain
 	     * socket */
-	    const struct plug_function_table *dummy = &dummy_plug;
-	    Socket s = sk_new(sk_addr_dup(ux), 0, 0, 0, 0, 0, (Plug)&dummy,
+	    Socket s = sk_new(sk_addr_dup(ux), 0, 0, 0, 0, 0, nullplug,
 	    #ifdef MPEXT
 	    0, 0
 	    #endif
@@ -450,16 +433,37 @@ static const char *x11_verify(unsigned long peer_ip, int peer_port,
     return NULL;
 }
 
+ptrlen BinarySource_get_string_xauth(BinarySource *src)
+{
+    size_t len = get_uint16(src);
+    return get_data(src, len);
+}
+#define get_string_xauth(src) \
+    BinarySource_get_string_xauth(BinarySource_UPCAST(src))
+
 void x11_get_auth_from_authfile(struct X11Display *disp,
 				const char *authfilename)
 {
     FILE *authfp;
-    char *buf, *ptr, *str[4];
-    int len[4];
+    char *buf;
+    int size;
+    BinarySource src[1];
     int family, protocol;
+    ptrlen addr, protoname, data;
+    char *displaynum_string;
+    int displaynum;
     int ideal_match = FALSE;
     char *ourhostname;
 
+    /* A maximally sized (wildly implausible) .Xauthority record
+     * consists of a 16-bit integer to start with, then four strings,
+     * each of which has a 16-bit length field followed by that many
+     * bytes of data (i.e. up to 0xFFFF bytes). */
+    const size_t MAX_RECORD_SIZE = 2 + 4 * (2+0xFFFF);
+
+    /* We'll want a buffer of twice that size (see below). */
+    const size_t BUF_SIZE = 2 * MAX_RECORD_SIZE;
+
     /*
      * Normally we should look for precisely the details specified in
      * `disp'. However, there's an oddity when the display is local:
@@ -489,29 +493,41 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
 
     ourhostname = get_hostname();
 
-    /* Records in .Xauthority contain four strings of up to 64K each */
-    buf = snewn(65537 * 4, char);
+    /*
+     * Allocate enough space to hold two maximally sized records, so
+     * that a full record can start anywhere in the first half. That
+     * way we avoid the accidentally-quadratic algorithm that would
+     * arise if we moved everything to the front of the buffer after
+     * consuming each record; instead, we only move everything to the
+     * front after our current position gets past the half-way mark.
+     * Before then, there's no need to move anyway; so this guarantees
+     * linear time, in that every byte written into this buffer moves
+     * at most once (because every move is from the second half of the
+     * buffer to the first half).
+     */
+    buf = snewn(BUF_SIZE, char);
+    size = fread(buf, 1, BUF_SIZE, authfp);
+    BinarySource_BARE_INIT(src, buf, size);
 
     while (!ideal_match) {
-	int c, i, j, match = FALSE;
-	
-#define GET do { c = fgetc(authfp); if (c == EOF) goto done; c = (unsigned char)c; } while (0)
-	/* Expect a big-endian 2-byte number giving address family */
-	GET; family = c;
-	GET; family = (family << 8) | c;
-	/* Then expect four strings, each composed of a big-endian 2-byte
-	 * length field followed by that many bytes of data */
-	ptr = buf;
-	for (i = 0; i < 4; i++) {
-	    GET; len[i] = c;
-	    GET; len[i] = (len[i] << 8) | c;
-	    str[i] = ptr;
-	    for (j = 0; j < len[i]; j++) {
-		GET; *ptr++ = c;
-	    }
-	    *ptr++ = '\0';
-	}
-#undef GET
+        int match = FALSE;
+
+        if (src->pos >= MAX_RECORD_SIZE) {
+            size -= src->pos;
+            memcpy(buf, buf + src->pos, size);
+            size += fread(buf + size, 1, BUF_SIZE - size, authfp);
+            BinarySource_BARE_INIT(src, buf, size);
+        }
+
+        family = get_uint16(src);
+        addr = get_string_xauth(src);
+        displaynum_string = mkstr(get_string_xauth(src));
+        displaynum = atoi(displaynum_string);
+        sfree(displaynum_string);
+        protoname = get_string_xauth(src);
+        data = get_string_xauth(src);
+        if (get_err(src))
+            break;
 
 	/*
 	 * Now we have a full X authority record in memory. See
@@ -525,7 +541,7 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
 	 *    connect to the display. 0 means IPv4; 6 means IPv6;
 	 *    256 means Unix-domain sockets.
 	 * 
-	 *  - str[0] is the network address itself. For IPv4 and
+	 *  - 'addr' is the network address itself. For IPv4 and
 	 *    IPv6, this is a string of binary data of the
 	 *    appropriate length (respectively 4 and 16 bytes)
 	 *    representing the address in big-endian format, e.g.
@@ -536,24 +552,23 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
 	 *    authority entries for Unix-domain displays on
 	 *    several machines without them clashing).
 	 * 
-	 *  - str[1] is the display number. I've no idea why
+	 *  - 'displaynum' is the display number. I've no idea why
 	 *    .Xauthority stores this as a string when it has a
 	 *    perfectly good integer format, but there we go.
 	 * 
-	 *  - str[2] is the authorisation method, encoded as its
-	 *    canonical string name (i.e. "MIT-MAGIC-COOKIE-1",
-	 *    "XDM-AUTHORIZATION-1" or something we don't
-	 *    recognise).
+	 *  - 'protoname' is the authorisation protocol, encoded as
+	 *    its canonical string name (i.e. "MIT-MAGIC-COOKIE-1",
+	 *    "XDM-AUTHORIZATION-1" or something we don't recognise).
 	 * 
-	 *  - str[3] is the actual authorisation data, stored in
+	 *  - 'data' is the actual authorisation data, stored in
 	 *    binary form.
 	 */
 
-	if (disp->displaynum < 0 || disp->displaynum != atoi(str[1]))
+	if (disp->displaynum < 0 || disp->displaynum != displaynum)
 	    continue;		       /* not the one */
 
 	for (protocol = 1; protocol < lenof(x11_authnames); protocol++)
-	    if (!strcmp(str[2], x11_authnames[protocol]))
+	    if (ptrlen_eq_string(protoname, x11_authnames[protocol]))
 		break;
 	if (protocol == lenof(x11_authnames))
 	    continue;  /* don't recognise this protocol, look for another */
@@ -564,7 +579,7 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
 		sk_addrtype(disp->addr) == ADDRTYPE_IPV4) {
 		char buf[4];
 		sk_addrcopy(disp->addr, buf);
-		if (len[0] == 4 && !memcmp(str[0], buf, 4)) {
+		if (addr.len == 4 && !memcmp(addr.ptr, buf, 4)) {
 		    match = TRUE;
 		    /* If this is a "localhost" entry, note it down
 		     * but carry on looking for a Unix-domain entry. */
@@ -577,7 +592,7 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
 		sk_addrtype(disp->addr) == ADDRTYPE_IPV6) {
 		char buf[16];
 		sk_addrcopy(disp->addr, buf);
-		if (len[0] == 16 && !memcmp(str[0], buf, 16)) {
+		if (addr.len == 16 && !memcmp(addr.ptr, buf, 16)) {
 		    match = TRUE;
 		    ideal_match = !localhost;
 		}
@@ -585,7 +600,7 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
 	    break;
 	  case 256: /* Unix-domain / localhost */
 	    if ((disp->unixdomain || localhost)
-	       	&& ourhostname && !strcmp(ourhostname, str[0]))
+                && ourhostname && ptrlen_eq_string(addr, ourhostname))
 		/* A matching Unix-domain socket is always the best
 		 * match. */
 		match = ideal_match = TRUE;
@@ -596,15 +611,14 @@ void x11_get_auth_from_authfile(struct X11Display *disp,
 	    /* Current best guess -- may be overridden if !ideal_match */
 	    disp->localauthproto = protocol;
 	    sfree(disp->localauthdata); /* free previous guess, if any */
-	    disp->localauthdata = snewn(len[3], unsigned char);
-	    memcpy(disp->localauthdata, str[3], len[3]);
-	    disp->localauthdatalen = len[3];
+	    disp->localauthdata = snewn(data.len, unsigned char);
+	    memcpy(disp->localauthdata, data.ptr, data.len);
+	    disp->localauthdatalen = data.len;
 	}
     }
 
-    done:
     fclose(authfp);
-    smemclr(buf, 65537 * 4);
+    smemclr(buf, 2 * MAX_RECORD_SIZE);
     sfree(buf);
     sfree(ourhostname);
 }
@@ -621,7 +635,8 @@ static void x11_send_init_error(struct X11Connection *conn,
 static void x11_closing(Plug plug, const char *error_msg, int error_code,
 			int calling_back)
 {
-    struct X11Connection *xconn = (struct X11Connection *) plug;
+    struct X11Connection *xconn = FROMFIELD(
+        plug, struct X11Connection, plugvt);
 
     if (error_msg) {
         /*
@@ -652,7 +667,8 @@ static void x11_closing(Plug plug, const char *error_msg, int error_code,
 
 static void x11_receive(Plug plug, int urgent, char *data, int len)
 {
-    struct X11Connection *xconn = (struct X11Connection *) plug;
+    struct X11Connection *xconn = FROMFIELD(
+        plug, struct X11Connection, plugvt);
 
     if (sshfwd_write(xconn->c, data, len) > 0) {
 	xconn->throttled = 1;
@@ -663,7 +679,8 @@ static void x11_receive(Plug plug, int urgent, char *data, int len)
 
 static void x11_sent(Plug plug, int bufsize)
 {
-    struct X11Connection *xconn = (struct X11Connection *) plug;
+    struct X11Connection *xconn = FROMFIELD(
+        plug, struct X11Connection, plugvt);
 
     sshfwd_unthrottle(xconn->c, bufsize);
 }
@@ -686,6 +703,14 @@ int x11_get_screen_number(char *display)
     return atoi(display + n + 1);
 }
 
+static const Plug_vtable X11Connection_plugvt = {
+    x11_log,
+    x11_closing,
+    x11_receive,
+    x11_sent,
+    NULL
+};
+
 /*
  * Called to set up the X11Connection structure, though this does not
  * yet connect to an actual server.
@@ -693,21 +718,13 @@ int x11_get_screen_number(char *display)
 struct X11Connection *x11_init(tree234 *authtree, void *c,
                                const char *peeraddr, int peerport)
 {
-    static const struct plug_function_table fn_table = {
-	x11_log,
-	x11_closing,
-	x11_receive,
-	x11_sent,
-	NULL
-    };
-
     struct X11Connection *xconn;
 
     /*
      * Open socket.
      */
     xconn = snew(struct X11Connection);
-    xconn->fn = &fn_table;
+    xconn->plugvt = &X11Connection_plugvt;
     xconn->auth_protocol = NULL;
     xconn->authtree = authtree;
     xconn->verified = 0;
@@ -791,7 +808,7 @@ static void x11_send_init_error(struct X11Connection *xconn,
     PUT_16BIT(xconn->firstpkt[0], reply + 6, msgsize >> 2);/* data len */
     memset(reply + 8, 0, msgsize);
     memcpy(reply + 8, full_message, msglen);
-    sshfwd_write(xconn->c, (char *)reply, 8 + msgsize);
+    sshfwd_write(xconn->c, reply, 8 + msgsize);
     sshfwd_write_eof(xconn->c);
     xconn->no_data_sent_to_x_client = FALSE;
     sfree(reply);
@@ -818,8 +835,10 @@ static int x11_parse_ip(const char *addr_string, unsigned long *ip)
 /*
  * Called to send data down the raw connection.
  */
-int x11_send(struct X11Connection *xconn, char *data, int len)
+int x11_send(struct X11Connection *xconn, const void *vdata, int len)
 {
+    const char *data = (const char *)vdata;
+
     if (!xconn)
 	return 0;
 
@@ -918,7 +937,7 @@ int x11_send(struct X11Connection *xconn, char *data, int len)
         xconn->disp = auth_matched->disp;
         xconn->s = new_connection(sk_addr_dup(xconn->disp->addr),
                                   xconn->disp->realhost, xconn->disp->port, 
-                                  0, 1, 0, 0, (Plug) xconn,
+                                  0, 1, 0, 0, &xconn->plugvt,
                                   sshfwd_get_conf(xconn->c));
         if ((err = sk_socket_error(xconn->s)) != NULL) {
             char *err_message = dupprintf("unable to connect to"
@@ -994,29 +1013,29 @@ void x11_send_eof(struct X11Connection *xconn)
  * representations of an X11 auth protocol name + hex cookie into our
  * usual integer protocol id and binary auth data.
  */
-int x11_identify_auth_proto(const char *protoname)
+int x11_identify_auth_proto(ptrlen protoname)
 {
     int protocol;
 
     for (protocol = 1; protocol < lenof(x11_authnames); protocol++)
-        if (!strcmp(protoname, x11_authnames[protocol]))
+        if (ptrlen_eq_string(protoname, x11_authnames[protocol]))
             return protocol;
     return -1;
 }
 
-void *x11_dehexify(const char *hex, int *outlen)
+void *x11_dehexify(ptrlen hexpl, int *outlen)
 {
     int len, i;
     unsigned char *ret;
 
-    len = strlen(hex) / 2;
+    len = hexpl.len / 2;
     ret = snewn(len, unsigned char);
 
     for (i = 0; i < len; i++) {
         char bytestr[3];
         unsigned val = 0;
-        bytestr[0] = hex[2*i];
-        bytestr[1] = hex[2*i+1];
+        bytestr[0] = ((const char *)hexpl.ptr)[2*i];
+        bytestr[1] = ((const char *)hexpl.ptr)[2*i+1];
         bytestr[2] = '\0';
         sscanf(bytestr, "%x", &val);
         ret[i] = val;
@@ -1065,8 +1084,7 @@ void *x11_make_greeting(int endian, int protomajor, int protominor,
         t = time(NULL);
         PUT_32BIT_MSB_FIRST(realauthdata+14, t);
 
-        des_encrypt_xdmauth((const unsigned char *)auth_data + 9,
-                            realauthdata, authdatalen);
+        des_encrypt_xdmauth((char *)auth_data + 9, realauthdata, authdatalen);
     } else {
         authdata = realauthdata;
         authdatalen = 0;

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä