diff --git a/3rdp/build/GNUmakefile b/3rdp/build/GNUmakefile
index ff3383339ba106d5f5e76b470c9d526c3d2994bf..f78f77eeaea6b9a5835421928e544deda144cb22 100644
--- a/3rdp/build/GNUmakefile
+++ b/3rdp/build/GNUmakefile
@@ -88,7 +88,7 @@ $(CRYPT_SRC): | $(3RDPSRCDIR)
 $(CRYPT_IDIR): | $(3RDPODIR)
 	$(QUIET)$(IFNOTEXIST) mkdir $(CRYPT_IDIR)
 
-$(CRYPTLIB_BUILD): $(3RDP_ROOT)/dist/cryptlib.zip $(3RDP_ROOT)/build/terminal-params.patch $(3RDP_ROOT)/build/cl-mingw32-static.patch $(3RDP_ROOT)/build/cl-ranlib.patch $(3RDP_ROOT)/build/cl-win32-noasm.patch $(3RDP_ROOT)/build/cl-zz-country.patch $(3RDP_ROOT)/build/cl-algorithms.patch $(3RDP_ROOT)/build/cl-allow-duplicate-ext.patch $(3RDP_ROOT)/build/cl-macosx-minver.patch $(3RDP_ROOT)/build/cl-endian.patch $(3RDP_ROOT)/build/cl-cryptodev.patch $(3RDP_ROOT)/build/cl-posix-me-gently.patch $(3RDP_ROOT)/build/cl-tpm-linux.patch $(3RDP_ROOT)/build/cl-PAM-noprompts.patch $(3RDP_ROOT)/build/cl-zlib.patch $(3RDP_ROOT)/build/Dynamic-linked-static-lib.patch $(3RDP_ROOT)/build/SSL-fix.patch $(3RDP_ROOT)/build/cl-bigger-maxattribute.patch $(3RDP_ROOT)/build/cl-vcxproj.patch $(3RDP_ROOT)/build/cl-mingw-vcver.patch $(3RDP_ROOT)/build/cl-win32-build-fix.patch $(3RDP_ROOT)/build/cl-gcc-non-const-time-val.patch $(3RDP_ROOT)/build/cl-no-odbc.patch $(3RDP_ROOT)/build/cl-noasm-defines.patch $(3RDP_ROOT)/build/cl-bn-noasm64-fix.patch $(3RDP_ROOT)/build/cl-no-RSA-suites.patch $(3RDP_ROOT)/build/cl-fix-ECC-RSA.patch $(3RDP_ROOT)/build/cl-prefer-ECC.patch $(3RDP_ROOT)/build/cl-prefer-ECC-harder.patch $(3RDP_ROOT)/build/cl-more-RSA-ECC-fixes.patch $(3RDP_ROOT)/build/cl-DH-key-init.patch $(3RDP_ROOT)/build/cl-clear-GCM-flag.patch $(3RDP_ROOT)/build/cl-use-ssh-ctr.patch $(3RDP_ROOT)/build/cl-ssh-list-ctr-modes.patch $(3RDP_ROOT)/build/cl-ssh-incCtr.patch $(3RDP_ROOT)/build/cl-ssl-suite-blocksizes.patch $(3RDP_ROOT)/build/cl-no-tpm.patch $(3RDP_ROOT)/build/cl-no-via-aes.patch $(3RDP_ROOT)/build/cl-fix-ssh-ecc-ephemeral.patch $(3RDP_ROOT)/build/cl-just-use-cc.patch $(3RDP_ROOT)/build/cl-learn-numbers.patch $(3RDP_ROOT)/build/cl-no-safe-stack.patch $(3RDP_ROOT)/build/cl-allow-pkcs12.patch $(3RDP_ROOT)/build/cl-uint64_t-redefine.patch $(3RDP_ROOT)/build/cl-random-openbsd.patch $(3RDP_ROOT)/build/cl-openbsd-threads.patch $(3RDP_ROOT)/build/cl-allow-none-auth.patch $(3RDP_ROOT)/build/cl-mingw-add-m32.patch $(3RDP_ROOT)/build/cl-poll-not-select.patch $(3RDP_ROOT)/build/cl-check-before-use.patch $(3RDP_ROOT)/build/cl-linux-yield.patch $(3RDP_ROOT)/build/cl-good-sockets.patch $(3RDP_ROOT)/build/cl-moar-objects.patch $(3RDP_ROOT)/build/cl-pthread_yield.patch $(3RDP_ROOT)/build/cl-check-cert-dont-modify.patch $(3RDP_ROOT)/build/cl-server-term-support.patch $(3RDP_ROOT)/build/cl-add-pubkey-attribute.patch | $(CRYPT_SRC) $(CRYPT_IDIR) $(3RDP_ROOT)/build/cl-remove-march.patch
+$(CRYPTLIB_BUILD): $(3RDP_ROOT)/dist/cryptlib.zip $(3RDP_ROOT)/build/terminal-params.patch $(3RDP_ROOT)/build/cl-mingw32-static.patch $(3RDP_ROOT)/build/cl-ranlib.patch $(3RDP_ROOT)/build/cl-win32-noasm.patch $(3RDP_ROOT)/build/cl-zz-country.patch $(3RDP_ROOT)/build/cl-algorithms.patch $(3RDP_ROOT)/build/cl-allow-duplicate-ext.patch $(3RDP_ROOT)/build/cl-macosx-minver.patch $(3RDP_ROOT)/build/cl-endian.patch $(3RDP_ROOT)/build/cl-cryptodev.patch $(3RDP_ROOT)/build/cl-posix-me-gently.patch $(3RDP_ROOT)/build/cl-tpm-linux.patch $(3RDP_ROOT)/build/cl-PAM-noprompts.patch $(3RDP_ROOT)/build/cl-zlib.patch $(3RDP_ROOT)/build/Dynamic-linked-static-lib.patch $(3RDP_ROOT)/build/SSL-fix.patch $(3RDP_ROOT)/build/cl-bigger-maxattribute.patch $(3RDP_ROOT)/build/cl-vcxproj.patch $(3RDP_ROOT)/build/cl-mingw-vcver.patch $(3RDP_ROOT)/build/cl-win32-build-fix.patch $(3RDP_ROOT)/build/cl-gcc-non-const-time-val.patch $(3RDP_ROOT)/build/cl-no-odbc.patch $(3RDP_ROOT)/build/cl-noasm-defines.patch $(3RDP_ROOT)/build/cl-bn-noasm64-fix.patch $(3RDP_ROOT)/build/cl-no-RSA-suites.patch $(3RDP_ROOT)/build/cl-fix-ECC-RSA.patch $(3RDP_ROOT)/build/cl-prefer-ECC.patch $(3RDP_ROOT)/build/cl-prefer-ECC-harder.patch $(3RDP_ROOT)/build/cl-more-RSA-ECC-fixes.patch $(3RDP_ROOT)/build/cl-DH-key-init.patch $(3RDP_ROOT)/build/cl-clear-GCM-flag.patch $(3RDP_ROOT)/build/cl-use-ssh-ctr.patch $(3RDP_ROOT)/build/cl-ssh-list-ctr-modes.patch $(3RDP_ROOT)/build/cl-ssh-incCtr.patch $(3RDP_ROOT)/build/cl-ssl-suite-blocksizes.patch $(3RDP_ROOT)/build/cl-no-tpm.patch $(3RDP_ROOT)/build/cl-no-via-aes.patch $(3RDP_ROOT)/build/cl-fix-ssh-ecc-ephemeral.patch $(3RDP_ROOT)/build/cl-just-use-cc.patch $(3RDP_ROOT)/build/cl-learn-numbers.patch $(3RDP_ROOT)/build/cl-no-safe-stack.patch $(3RDP_ROOT)/build/cl-allow-pkcs12.patch $(3RDP_ROOT)/build/cl-uint64_t-redefine.patch $(3RDP_ROOT)/build/cl-random-openbsd.patch $(3RDP_ROOT)/build/cl-openbsd-threads.patch $(3RDP_ROOT)/build/cl-allow-none-auth.patch $(3RDP_ROOT)/build/cl-mingw-add-m32.patch $(3RDP_ROOT)/build/cl-poll-not-select.patch $(3RDP_ROOT)/build/cl-check-before-use.patch $(3RDP_ROOT)/build/cl-linux-yield.patch $(3RDP_ROOT)/build/cl-good-sockets.patch $(3RDP_ROOT)/build/cl-moar-objects.patch $(3RDP_ROOT)/build/cl-pthread_yield.patch $(3RDP_ROOT)/build/cl-check-cert-dont-modify.patch $(3RDP_ROOT)/build/cl-server-term-support.patch $(3RDP_ROOT)/build/cl-add-pubkey-attribute.patch $(3RDP_ROOT)/build/cl-allow-ssh-auth-retries.patch | $(CRYPT_SRC) $(CRYPT_IDIR) $(3RDP_ROOT)/build/cl-remove-march.patch
 	@echo Creating $@ ...
 	$(QUIET)-rm -rf $(CRYPT_SRC)/*
 	$(QUIET)unzip -oa $(3RDPDISTDIR)/cryptlib.zip -d $(CRYPT_SRC)
@@ -149,6 +149,7 @@ $(CRYPTLIB_BUILD): $(3RDP_ROOT)/dist/cryptlib.zip $(3RDP_ROOT)/build/terminal-pa
 	$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-pthread_yield.patch
 	$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-server-term-support.patch
 	$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-add-pubkey-attribute.patch
+	$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-allow-ssh-auth-retries.patch
 ifeq ($(os),win32)
 	$(QUIET)cd $(CRYPT_SRC) && env - PATH="$(PATH)" CC="$(CC)" AR="$(AR)" RANLIB="$(RANLIB)" make directories
 	$(QUIET)cd $(CRYPT_SRC) && env - PATH="$(PATH)" CC="$(CC)" AR="$(AR)" RANLIB="$(RANLIB)" make toolscripts
diff --git a/3rdp/build/cl-allow-ssh-auth-retries.patch b/3rdp/build/cl-allow-ssh-auth-retries.patch
new file mode 100644
index 0000000000000000000000000000000000000000..46f3354b18b85fb83adb1fdf857c077d8a829db0
--- /dev/null
+++ b/3rdp/build/cl-allow-ssh-auth-retries.patch
@@ -0,0 +1,518 @@
+--- ./session/ssh2_authc.c.orig	2023-12-28 09:41:49.741680000 -0500
++++ ./session/ssh2_authc.c	2023-12-28 09:48:19.999152000 -0500
+@@ -583,7 +583,5 @@
+ 					  "Server requested password authentication but only a "
+ 					  "public/private key was available" ) );
+ 			}
+-		retExt( CRYPT_ERROR_WRONGKEY,
+-				( CRYPT_ERROR_WRONGKEY, SESSION_ERRINFO, 
+-				  "Server reported: Invalid public-key authentication" ) );
++		return CRYPT_ENVELOPE_RESOURCE;
+		}
+--- session/sess_attr.c.orig	2023-12-29 10:12:33.284671000 -0500
++++ session/sess_attr.c	2023-12-29 11:34:14.207575000 -0500
+@@ -292,6 +292,7 @@
+ 		   of, and leads to exceptions to exceptions, so we keep it simple 
+ 		   and only allow passwords to be added if there's an immediately 
+ 		   preceding username */
++#if 0
+ 		if( cryptStatusError( status ) )
+ 			{
+ 			return( exitErrorNotInited( sessionInfoPtr, 
+@@ -305,6 +301,7 @@
+ 			return( exitErrorNotInited( sessionInfoPtr, 
+ 										CRYPT_SESSINFO_USERNAME ) );
+ 			}
++#endif
+ 		}
+ 
+ 	/* If it could be an encoded PKI value, check its validity */
+--- kernel/attr_acl.c.orig	2023-12-29 11:53:27.990291000 -0500
++++ kernel/attr_acl.c	2023-12-29 11:54:01.468829000 -0500
+@@ -3655,7 +3655,7 @@
+ 		subACL_SessinfoKeyset ),
+ 	MKACL_EX(	/* Session authorisation OK */
+ 		CRYPT_SESSINFO_AUTHRESPONSE, ATTRIBUTE_VALUE_NUMERIC,
+-		ST_NONE, ST_NONE, ST_SESS_SSL | ST_SESS_SSL_SVR | ST_SESS_SSH_SVR, 
++		ST_NONE, ST_NONE, ST_SESS_SSL | ST_SESS_SSL_SVR | ST_SESS_SSH | ST_SESS_SSH_SVR, 
+ 		MKPERM_SESSIONS( RWx_RWx ), 0,
+ 		ROUTE( OBJECT_TYPE_SESSION ),
+ 		RANGE_ALLOWEDVALUES, allowedAuthResponses ),
+--- session/ssh.c.orig	2023-12-29 12:02:24.938661000 -0500
++++ session/ssh.c	2023-12-29 12:05:44.619757000 -0500
+@@ -861,6 +861,18 @@
+ 
+ 	REQUIRES( sanityCheckSessionSSH( sessionInfoPtr ) );
+ 
++	/* If we're completing a handshake that was interrupted while we got
++	   confirmation of the client auth, skip the initial handshake stages
++	   and go straight to the handshake completion stage */
++	if( TEST_FLAG( sessionInfoPtr->flags, SESSION_FLAG_PARTIALOPEN ) )
++		{
++		SSH_HANDSHAKE_INFO handshakeInfo;
++
++		initHandshakeInfo( &handshakeInfo );
++		initSSH2clientProcessing( &handshakeInfo );
++		return( completeHandshake( sessionInfoPtr, &handshakeInfo ) );
++		}
++
+ 	shutdownFunction = ( SES_SHUTDOWN_FUNCTION ) \
+ 					   FNPTR_GET( sessionInfoPtr->shutdownFunction );
+ 	REQUIRES( shutdownFunction != NULL );
+--- session/ssh2_cli.c.orig	2018-12-21 04:12:46.000000000 -0500
++++ session/ssh2_cli.c	2023-12-29 12:33:18.988457000 -0500
+@@ -963,230 +963,238 @@
+ 
+ 	REQUIRES( sanityCheckSessionSSH( sessionInfoPtr ) );
+ 
+-	/* Set up the security information required for the session */
+-	status = initSecurityInfo( sessionInfoPtr, handshakeInfo );
+-	if( cryptStatusError( status ) )
+-		return( status );
+-	CFI_CHECK_UPDATE( "initSecurityInfo" );
+-
+-	/* Build our change cipherspec message and request authentication with
+-	   the server:
+-
+-		byte	type = SSH_MSG_NEWKEYS
+-		...
+-
+-	   After this point the write channel is in the secure state, so we 
+-	   switch from wrapPlaintextPacketSSH2() to wrapPacketSSH2() */
+-	status = openPacketStreamSSH( &stream, sessionInfoPtr, SSH_MSG_NEWKEYS );
+-	if( cryptStatusError( status ) )
+-		return( status );
+-	status = wrapPlaintextPacketSSH2( sessionInfoPtr, &stream, 0 );
+-	if( cryptStatusError( status ) )
++	if( !TEST_FLAG( sessionInfoPtr->flags, SESSION_FLAG_PARTIALOPEN ) )
+ 		{
+-		sMemDisconnect( &stream );
+-		return( status );
+-		}
+-	SET_FLAG( sessionInfoPtr->flags, SESSION_FLAG_ISSECURE_WRITE );
+-	CFI_CHECK_UPDATE( "SSH_MSG_NEWKEYS" );
++		/* Set up the security information required for the session */
++		status = initSecurityInfo( sessionInfoPtr, handshakeInfo );
++		if( cryptStatusError( status ) )
++			return( status );
++		CFI_CHECK_UPDATE( "initSecurityInfo" );
+ 
+-#if 0
+-	/*   byte       SSH_MSG_EXT_INFO
+-	     uint32     nr-extensions
+-	       string   extension-name
+-	       string   extension-value (binary) */
+-	status = continuePacketStreamSSH( &stream, SSH_MSG_EXT_INFO, 
+-									  &packetOffset );
+-	if( cryptStatusOK( status ) )
+-		{
+-		writeUint32( &stream, 1 );
+-		writeString32( &stream, "global-requests-ok", 18 );
+-		status = writeUint32( &stream, 0 );
+-		}
+-	if( cryptStatusOK( status ) )
+-		{
+-		status = wrapPacketSSH2( sessionInfoPtr, &stream, packetOffset, 
+-								 FALSE );
+-		}
+-#endif /* Test handling of trigger for global request after authentication */ 
++		/* Build our change cipherspec message and request authentication with
++		   the server:
+ 
+-	/*	...
+-		byte	type = SSH_MSG_SERVICE_REQUEST
+-		string	service_name = "ssh-userauth".
+-		
+-	   For some reason SSH requires the use of two authentication messages, 
+-	   an "I'm about to authenticate" packet and an "I'm authenticating" 
+-	   packet, so we have to perform the authentication in two parts (dum 
+-	   loquimur, fugerit invida aetas) */
+-	status = continuePacketStreamSSH( &stream, SSH_MSG_SERVICE_REQUEST,
+-									  &packetOffset );
+-	if( cryptStatusOK( status ) )
+-		status = writeString32( &stream, "ssh-userauth", 12 );
+-	if( cryptStatusOK( status ) )
+-		{
+-		status = wrapPacketSSH2( sessionInfoPtr, &stream, packetOffset, 
+-								 FALSE );
+-		}
+-	if( cryptStatusError( status ) )
+-		{
+-		sMemDisconnect( &stream );
+-		return( status );
+-		}
+-	CFI_CHECK_UPDATE( "SSH_MSG_SERVICE_REQUEST" );
++			byte	type = SSH_MSG_NEWKEYS
++			...
+ 
+-	/* Send the whole mess to the server.  This is yet another place where 
+-	   the SSH spec's vagueness over message ordering causes problems.  SSL 
+-	   at this point uses a Finished message in which the client and server 
+-	   do a mutual proof-of-possession of encryption and MAC keys via a 
+-	   pipeline-stalling message that prevents any further (sensitive) data 
+-	   from being exchanged until the PoP has concluded (the SSL Finished 
+-	   also authenticates the handshake messages) but SSH doesn't have any
+-	   such requirements.  The signed exchange hash from the server proves 
+-	   to the client that the server knows the master secret but not 
+-	   necessarily that the client and server share encryption and MAC keys.  
+-	   Without this mutual PoP the client could potentially end up sending 
+-	   passwords to the server using an incorrect (and potentially weak) key 
+-	   if it's messed up and derived the key incorrectly.  Although mutual 
+-	   PoP isn't a design goal of the SSH handshake we do it anyway (as far 
+-	   as we can without a proper Finished message), although this 
+-	   introduces a pipeline stall at this point.
+-	   
+-	   In addition because of the aforementioned ambiguity over message 
+-	   ordering we have to send our change cipherspec first because some 
+-	   implementations will stop and wait before they send their one, so if 
+-	   they don't see our one first they lock up.  To make this even more 
+-	   entertaining these are typically older ssh.com implementations with a 
+-	   whole smorgasbord of handshaking and crypto bugs, because of the lack 
+-	   of PoP and the fact that we have to send the first encrypted/MACd
+-	   message, encountering any of these bugs results in garbage from the
+-	   server followed by a closed connection with no ability to diagnose 
+-	   the problem.
+-
+-	   Complicating things even further is the fact that the SSH extension
+-	   request is sent immediately after the change cipherspec, before
+-	   receiving confirmation that that crypto has been correctly set up.
+-	   This is because of some vague desire to provide for the insertion
+-	   of extensions before the userauth is performed in case there's a 
+-	   need to exchange extensions that may affect the authentication 
+-	   process.  What this applies to in practice is "server-sig-algs",
+-	   which defines the algorithms that the server will accept for 
+-	   "publickey"-format userauth.
+-
+-	   The spec in fact says that after a key exchange with implicit server 
+-	   authentication the client has to wait for the server to send a 
+-	   service-accept packet before continuing, however it never explains 
+-	   what implicit (and, by extension, explicit) server authentication 
+-	   actually are.  This text is a leftover from an extremely early SSH 
+-	   draft in which the only keyex mechanism was "double-encrypting-sha", 
+-	   a mechanism that required a pipeline stall at this point because the 
+-	   client wasn't able to authenticate the server until it received the 
+-	   first encrypted/MAC'ed message from it.  To extricate ourselves from 
+-	   the confusion due to the missing definition we could define "implicit 
+-	   authentication" to be "Something completely different from what we're 
+-	   doing here" which means that we could send the two packets together 
+-	   without having to wait for the server, but it's probably better to 
+-	   use SSL-tyle Finished semantics at this point even if it adds an 
+-	   extra RTT delay */
+-	status = sendPacketSSH2( sessionInfoPtr, &stream );
+-	sMemDisconnect( &stream );
+-	if( cryptStatusError( status ) )
+-		return( status );
+-
+-	/* Wait for the server's change cipherspec message.  From this point
+-	   on the read channel is also in the secure state */
+-	status = readHSPacketSSH2( sessionInfoPtr, SSH_MSG_NEWKEYS, ID_SIZE );
+-	if( cryptStatusError( status ) )
+-		return( status );
+-	SET_FLAG( sessionInfoPtr->flags, SESSION_FLAG_ISSECURE_READ );
+-	CFI_CHECK_UPDATE( "readHSPacketSSH2" );
+-
+-	/* Wait for the server's service-accept message that should follow in
+-	   response to the service request sent after out change cipherspec.  
+-	   Some buggy versions send an empty service-accept packet so we only 
+-	   check the contents if it's a correctly-formatted packet */
+-	if( TEST_FLAG( sessionInfoPtr->protocolFlags, 
+-				   SSH_PFLAG_EMPTYSVCACCEPT ) )
+-		{
+-		/* It's a buggy implementation, just check for the presence of a 
+-		   packet without looking at the contents */
+-		status = readHSPacketSSH2( sessionInfoPtr, SSH_MSG_SERVICE_ACCEPT, 
+-								   ID_SIZE );
++		   After this point the write channel is in the secure state, so we 
++		   switch from wrapPlaintextPacketSSH2() to wrapPacketSSH2() */
++		status = openPacketStreamSSH( &stream, sessionInfoPtr, SSH_MSG_NEWKEYS );
+ 		if( cryptStatusError( status ) )
++			return( status );
++		status = wrapPlaintextPacketSSH2( sessionInfoPtr, &stream, 0 );
++		if( cryptStatusError( status ) )
+ 			{
+-			/* This is the first message after the change cipherspec, a 
+-			   basic packet format error is more likely to be due to an 
+-			   incorrect key than an actual format error */
+-			retExtErr( CRYPT_ERROR_WRONGKEY,
+-					   ( CRYPT_ERROR_WRONGKEY, SESSION_ERRINFO, 
+-						 SESSION_ERRINFO, 
+-						 "Invalid packet data for SSH_MSG_SERVICE_ACCEPT, "
+-						 "probably due to incorrect encryption keys being "
+-						 "negotiated during the handshake:" ) );
++			sMemDisconnect( &stream );
++			return( status );
+ 			}
+-		}
+-	else
+-		{
+-		int length;
++		SET_FLAG( sessionInfoPtr->flags, SESSION_FLAG_ISSECURE_WRITE );
++		CFI_CHECK_UPDATE( "SSH_MSG_NEWKEYS" );
+ 
+-		/* Check the service-accept packet:
++	#if 0
++		/*   byte       SSH_MSG_EXT_INFO
++		     uint32     nr-extensions
++		       string   extension-name
++		       string   extension-value (binary) */
++		status = continuePacketStreamSSH( &stream, SSH_MSG_EXT_INFO, 
++										  &packetOffset );
++		if( cryptStatusOK( status ) )
++			{
++			writeUint32( &stream, 1 );
++			writeString32( &stream, "global-requests-ok", 18 );
++			status = writeUint32( &stream, 0 );
++			}
++		if( cryptStatusOK( status ) )
++			{
++			status = wrapPacketSSH2( sessionInfoPtr, &stream, packetOffset, 
++									 FALSE );
++			}
++	#endif /* Test handling of trigger for global request after authentication */ 
+ 
+-			byte	type = SSH_MSG_SERVICE_ACCEPT
++		/*	...
++			byte	type = SSH_MSG_SERVICE_REQUEST
+ 			string	service_name = "ssh-userauth".
+ 			
+-		   This may also be an extension info packet if the server is using 
+-		   extensions: 
+-		   
+-			byte		type = SSH_MSG_EXT_INFO
+-			uint32		no_extensions
+-				string	name
+-				string	value (binary data) */
+-		status = length = \
+-			readHSPacketSSH2( sessionInfoPtr, SSH_MSG_SPECIAL_SERVICEACCEPT,
+-							  ID_SIZE + UINT32_SIZE );
+-		if( cryptStatusError( status ) )
++		   For some reason SSH requires the use of two authentication messages, 
++		   an "I'm about to authenticate" packet and an "I'm authenticating" 
++		   packet, so we have to perform the authentication in two parts (dum 
++		   loquimur, fugerit invida aetas) */
++		status = continuePacketStreamSSH( &stream, SSH_MSG_SERVICE_REQUEST,
++										  &packetOffset );
++		if( cryptStatusOK( status ) )
++			status = writeString32( &stream, "ssh-userauth", 12 );
++		if( cryptStatusOK( status ) )
+ 			{
+-			/* This is the first message after the change cipherspec, a 
+-			   basic packet format error is more likely to be due to an 
+-			   incorrect key than an actual format error */
+-			retExtErr( CRYPT_ERROR_WRONGKEY,
+-					   ( CRYPT_ERROR_WRONGKEY, SESSION_ERRINFO, 
+-						 SESSION_ERRINFO, 
+-						 "Invalid packet data for SSH_MSG_SERVICE_ACCEPT, "
+-						 "probably due to incorrect encryption keys being "
+-						 "negotiated during the handshake:" ) );
++			status = wrapPacketSSH2( sessionInfoPtr, &stream, packetOffset, 
++									 FALSE );
+ 			}
+-		if( sessionInfoPtr->sessionSSH->packetType == SSH_MSG_EXT_INFO )
++		if( cryptStatusError( status ) )
+ 			{
+-			/* The server sent extension information, process it */
+-			sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
+-			status = readExtensionsSSH( sessionInfoPtr, &stream );
+ 			sMemDisconnect( &stream );
++			return( status );
++			}
++		CFI_CHECK_UPDATE( "SSH_MSG_SERVICE_REQUEST" );
++
++		/* Send the whole mess to the server.  This is yet another place where 
++		   the SSH spec's vagueness over message ordering causes problems.  SSL 
++		   at this point uses a Finished message in which the client and server 
++		   do a mutual proof-of-possession of encryption and MAC keys via a 
++		   pipeline-stalling message that prevents any further (sensitive) data 
++		   from being exchanged until the PoP has concluded (the SSL Finished 
++		   also authenticates the handshake messages) but SSH doesn't have any
++		   such requirements.  The signed exchange hash from the server proves 
++		   to the client that the server knows the master secret but not 
++		   necessarily that the client and server share encryption and MAC keys.  
++		   Without this mutual PoP the client could potentially end up sending 
++		   passwords to the server using an incorrect (and potentially weak) key 
++		   if it's messed up and derived the key incorrectly.  Although mutual 
++		   PoP isn't a design goal of the SSH handshake we do it anyway (as far 
++		   as we can without a proper Finished message), although this 
++		   introduces a pipeline stall at this point.
++		   
++		   In addition because of the aforementioned ambiguity over message 
++		   ordering we have to send our change cipherspec first because some 
++		   implementations will stop and wait before they send their one, so if 
++		   they don't see our one first they lock up.  To make this even more 
++		   entertaining these are typically older ssh.com implementations with a 
++		   whole smorgasbord of handshaking and crypto bugs, because of the lack 
++		   of PoP and the fact that we have to send the first encrypted/MACd
++		   message, encountering any of these bugs results in garbage from the
++		   server followed by a closed connection with no ability to diagnose 
++		   the problem.
++
++		   Complicating things even further is the fact that the SSH extension
++		   request is sent immediately after the change cipherspec, before
++		   receiving confirmation that that crypto has been correctly set up.
++		   This is because of some vague desire to provide for the insertion
++		   of extensions before the userauth is performed in case there's a 
++		   need to exchange extensions that may affect the authentication 
++		   process.  What this applies to in practice is "server-sig-algs",
++		   which defines the algorithms that the server will accept for 
++		   "publickey"-format userauth.
++
++		   The spec in fact says that after a key exchange with implicit server 
++		   authentication the client has to wait for the server to send a 
++		   service-accept packet before continuing, however it never explains 
++		   what implicit (and, by extension, explicit) server authentication 
++		   actually are.  This text is a leftover from an extremely early SSH 
++		   draft in which the only keyex mechanism was "double-encrypting-sha", 
++		   a mechanism that required a pipeline stall at this point because the 
++		   client wasn't able to authenticate the server until it received the 
++		   first encrypted/MAC'ed message from it.  To extricate ourselves from 
++		   the confusion due to the missing definition we could define "implicit 
++		   authentication" to be "Something completely different from what we're 
++		   doing here" which means that we could send the two packets together 
++		   without having to wait for the server, but it's probably better to 
++		   use SSL-tyle Finished semantics at this point even if it adds an 
++		   extra RTT delay */
++		status = sendPacketSSH2( sessionInfoPtr, &stream );
++		sMemDisconnect( &stream );
++		if( cryptStatusError( status ) )
++			return( status );
++
++		/* Wait for the server's change cipherspec message.  From this point
++		   on the read channel is also in the secure state */
++		status = readHSPacketSSH2( sessionInfoPtr, SSH_MSG_NEWKEYS, ID_SIZE );
++		if( cryptStatusError( status ) )
++			return( status );
++		SET_FLAG( sessionInfoPtr->flags, SESSION_FLAG_ISSECURE_READ );
++		CFI_CHECK_UPDATE( "readHSPacketSSH2" );
++
++		/* Wait for the server's service-accept message that should follow in
++		   response to the service request sent after out change cipherspec.  
++		   Some buggy versions send an empty service-accept packet so we only 
++		   check the contents if it's a correctly-formatted packet */
++		if( TEST_FLAG( sessionInfoPtr->protocolFlags, 
++					   SSH_PFLAG_EMPTYSVCACCEPT ) )
++			{
++			/* It's a buggy implementation, just check for the presence of a 
++			   packet without looking at the contents */
++			status = readHSPacketSSH2( sessionInfoPtr, SSH_MSG_SERVICE_ACCEPT, 
++									   ID_SIZE );
+ 			if( cryptStatusError( status ) )
+-				return( status );
++				{
++				/* This is the first message after the change cipherspec, a 
++				   basic packet format error is more likely to be due to an 
++				   incorrect key than an actual format error */
++				retExtErr( CRYPT_ERROR_WRONGKEY,
++						   ( CRYPT_ERROR_WRONGKEY, SESSION_ERRINFO, 
++							 SESSION_ERRINFO, 
++							 "Invalid packet data for SSH_MSG_SERVICE_ACCEPT, "
++							 "probably due to incorrect encryption keys being "
++							 "negotiated during the handshake:" ) );
++				}
++			}
++		else
++			{
++			int length;
+ 
+-			/* Retry the service-accept read */
++			/* Check the service-accept packet:
++
++				byte	type = SSH_MSG_SERVICE_ACCEPT
++				string	service_name = "ssh-userauth".
++				
++			   This may also be an extension info packet if the server is using 
++			   extensions: 
++			   
++				byte		type = SSH_MSG_EXT_INFO
++				uint32		no_extensions
++					string	name
++					string	value (binary data) */
+ 			status = length = \
+-				readHSPacketSSH2( sessionInfoPtr, SSH_MSG_SERVICE_ACCEPT,
+-								  ID_SIZE + sizeofString32( 8 ) );
++				readHSPacketSSH2( sessionInfoPtr, SSH_MSG_SPECIAL_SERVICEACCEPT,
++								  ID_SIZE + UINT32_SIZE );
+ 			if( cryptStatusError( status ) )
+-				return( status );
++				{
++				/* This is the first message after the change cipherspec, a 
++				   basic packet format error is more likely to be due to an 
++				   incorrect key than an actual format error */
++				retExtErr( CRYPT_ERROR_WRONGKEY,
++						   ( CRYPT_ERROR_WRONGKEY, SESSION_ERRINFO, 
++							 SESSION_ERRINFO, 
++							 "Invalid packet data for SSH_MSG_SERVICE_ACCEPT, "
++							 "probably due to incorrect encryption keys being "
++							 "negotiated during the handshake:" ) );
++				}
++			if( sessionInfoPtr->sessionSSH->packetType == SSH_MSG_EXT_INFO )
++				{
++				/* The server sent extension information, process it */
++				sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
++				status = readExtensionsSSH( sessionInfoPtr, &stream );
++				sMemDisconnect( &stream );
++				if( cryptStatusError( status ) )
++					return( status );
++
++				/* Retry the service-accept read */
++				status = length = \
++					readHSPacketSSH2( sessionInfoPtr, SSH_MSG_SERVICE_ACCEPT,
++									  ID_SIZE + sizeofString32( 8 ) );
++				if( cryptStatusError( status ) )
++					return( status );
++				}
++			sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
++			status = readString32( &stream, stringBuffer, CRYPT_MAX_TEXTSIZE,
++								   &stringLength );
++			sMemDisconnect( &stream );
++			if( cryptStatusError( status ) || \
++				stringLength != 12 || \
++				memcmp( stringBuffer, "ssh-userauth", 12 ) )
++				{
++				/* More of a sanity check than anything else, the MAC should 
++				   have caught any keying problems */
++				retExt( CRYPT_ERROR_BADDATA,
++						( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
++						  "Invalid service accept packet" ) );
++				}
+ 			}
+-		sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
+-		status = readString32( &stream, stringBuffer, CRYPT_MAX_TEXTSIZE,
+-							   &stringLength );
+-		sMemDisconnect( &stream );
+-		if( cryptStatusError( status ) || \
+-			stringLength != 12 || \
+-			memcmp( stringBuffer, "ssh-userauth", 12 ) )
+-			{
+-			/* More of a sanity check than anything else, the MAC should 
+-			   have caught any keying problems */
+-			retExt( CRYPT_ERROR_BADDATA,
+-					( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, 
+-					  "Invalid service accept packet" ) );
+-			}
+-		}
+-	CFI_CHECK_UPDATE( "serviceAccept" );
++		CFI_CHECK_UPDATE( "serviceAccept" );
+ 
++		REQUIRES( CFI_CHECK_SEQUENCE_5( "initSecurityInfo", "SSH_MSG_NEWKEYS",
++										"SSH_MSG_SERVICE_REQUEST", 
++										"readHSPacketSSH2", "serviceAccept") );
++		CFI_CHECK_VALUE = CFI_CHECK_INIT;
++	}
++
+ 	/* Try and authenticate ourselves to the server */
+ 	status = processClientAuth( sessionInfoPtr, handshakeInfo );
+ 	if( cryptStatusError( status ) )
+@@ -1210,11 +1218,7 @@
+ 		return( status );
+ 	CFI_CHECK_UPDATE( "sendChannelOpen" );
+ 
+-	REQUIRES( CFI_CHECK_SEQUENCE_7( "initSecurityInfo", "SSH_MSG_NEWKEYS",
+-									"SSH_MSG_SERVICE_REQUEST", 
+-									"readHSPacketSSH2", "serviceAccept",
+-									"processClientAuth", 
+-									"sendChannelOpen" ) );
++	REQUIRES( CFI_CHECK_SEQUENCE_2(  "processClientAuth", "sendChannelOpen" ) );
+ 	return( CRYPT_OK );
+ #else	/* Test handling of OpenSSH "no-more-sessions@openssh.com" */
+ 	status = sendChannelOpen( sessionInfoPtr );
diff --git a/src/syncterm/ssh.c b/src/syncterm/ssh.c
index df7be2d4fee851f86e7c4285a147d6b1a4be2c6e..b2ca8d0bc44a478c0ce66508b13ad47fe3737f9e 100644
--- a/src/syncterm/ssh.c
+++ b/src/syncterm/ssh.c
@@ -306,7 +306,6 @@ key_not_present(sftp_filehandle_t f, const char *priv)
 			if (!sftpc_read(sftp_state, f, off, (bufsz - bufpos > 1024) ? 1024 : bufsz - bufpos, &r)) {
 				if (sftp_state->err_code == SSH_FX_EOF) {
 					free(buf);
-					free_sftp_str(r);
 					return true;
 				}
 				free(buf);
@@ -347,7 +346,6 @@ add_public_key(struct bbslist *bbs, char *priv)
 	bool added = false;
 
 	// TODO: Without this sleep, all is woe.
-	//SLEEP(10);
 	while (!conn_api.input_thread_running)
 		SLEEP(1);
 	if (!bbs->hidepopups) {
@@ -584,7 +582,7 @@ ssh_connect(struct bbslist *bbs)
 		}
 	}
 	else {
-		if (!password[0]/* && ssh_context == -1*/) {
+		if (!password[0] && ssh_context == -1) {
 			if (bbs->hidepopups)
 				init_uifc(false, false);
 			uifcinput("Password", MAX_PASSWD_LEN, password, K_PASSWORD, "Incorrect password.  Try again.");
@@ -661,7 +659,38 @@ ssh_connect(struct bbslist *bbs)
 		uifc.pop(NULL);
 		uifc.pop("Activating Session");
 	}
-	status = cl.SetAttribute(ssh_session, CRYPT_SESSINFO_ACTIVE, 1);
+
+	do {
+		status = cl.SetAttribute(ssh_session, CRYPT_SESSINFO_ACTIVE, 1);
+		if (status == CRYPT_ENVELOPE_RESOURCE) {
+			int status2;
+			status2 = cl.GetAttributeString(ssh_session, CRYPT_SESSINFO_USERNAME, username, &rows);
+			fprintf(stderr, "Stats @ %d: %d\n", __LINE__, status2);
+			if (cryptStatusOK(status2)) {
+				username[rows] = 0;
+				fprintf(stderr, "Len=%d\n", rows);
+				fprintf(stderr, "Username: '%s'\n", username);
+			}
+			//status2 = cl.DeleteAttribute(ssh_session, CRYPT_SESSINFO_PRIVATEKEY);
+			//fprintf(stderr, "Stats @ %d: %d\n", __LINE__, status2); // -21 permission... can't delete...
+			//status2 = cl.DeleteAttribute(ssh_session, CRYPT_SESSINFO_USERNAME);
+			//fprintf(stderr, "Stats @ %d: %d\n", __LINE__, status2); // Done...
+			status2 = cl.DeleteAttribute(ssh_session, CRYPT_SESSINFO_PASSWORD);
+			fprintf(stderr, "Stats @ %d: %d\n", __LINE__, status2);
+			if (bbs->hidepopups)
+				init_uifc(false, false);
+			password[0] = 0;
+			uifcinput("Password", MAX_PASSWD_LEN, password, K_PASSWORD, "Incorrect password.  Try again.");
+			if (bbs->hidepopups)
+				uifcbail();
+			//status2 = cl.SetAttributeString(ssh_session, CRYPT_SESSINFO_USERNAME, username, strlen(username));
+			//fprintf(stderr, "Stats @ %d: %d\n", __LINE__, status2);
+			status2 = cl.SetAttributeString(ssh_session, CRYPT_SESSINFO_PASSWORD, password, strlen(password));
+			fprintf(stderr, "Stats @ %d: %d\n", __LINE__, status2);
+			status2 = cl.SetAttribute(ssh_session, CRYPT_SESSINFO_AUTHRESPONSE, 1);
+			fprintf(stderr, "Stats @ %d: %d\n", __LINE__, status2);
+		}
+	} while (status == CRYPT_ENVELOPE_RESOURCE);
 	if (cryptStatusError(status)) {
 		free(pubkey);
 		error_popup(bbs, "activating session", status);