Skip to content
Snippets Groups Projects
Commit 2c036f74 authored by Deucе's avatar Deucе :ok_hand_tone4:
Browse files

Allow retrying SSH client auth.

If you initially try private key auth for example, allow retrying
with password auth and vice-versa.  This is needed for reasonable
fallback from private-key auth since we don't know in advance
which will work, and we don't want to disconnect an reconnect.

This takes the guardrails of adding a password.  Previously, it was
ensured that a password was always added immediately after a username.
This is to ensure when using the server for example and stuffing a
list full of all the user/pw pairs, each pw goes with a user.

Instead of returning an error, this now allows adding passwords
without a corrsponding user that won't be used, and there will be
no indication of why.

Death Star mode for this feature is better than not having fallback
in my opinion.
parent ccfb0f02
No related branches found
No related tags found
1 merge request!455Update branch with changes from master
...@@ -88,7 +88,7 @@ $(CRYPT_SRC): | $(3RDPSRCDIR) ...@@ -88,7 +88,7 @@ $(CRYPT_SRC): | $(3RDPSRCDIR)
$(CRYPT_IDIR): | $(3RDPODIR) $(CRYPT_IDIR): | $(3RDPODIR)
$(QUIET)$(IFNOTEXIST) mkdir $(CRYPT_IDIR) $(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 $@ ... @echo Creating $@ ...
$(QUIET)-rm -rf $(CRYPT_SRC)/* $(QUIET)-rm -rf $(CRYPT_SRC)/*
$(QUIET)unzip -oa $(3RDPDISTDIR)/cryptlib.zip -d $(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 ...@@ -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-pthread_yield.patch
$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-server-term-support.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-add-pubkey-attribute.patch
$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-allow-ssh-auth-retries.patch
ifeq ($(os),win32) 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 directories
$(QUIET)cd $(CRYPT_SRC) && env - PATH="$(PATH)" CC="$(CC)" AR="$(AR)" RANLIB="$(RANLIB)" make toolscripts $(QUIET)cd $(CRYPT_SRC) && env - PATH="$(PATH)" CC="$(CC)" AR="$(AR)" RANLIB="$(RANLIB)" make toolscripts
......
--- ./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 );
...@@ -306,7 +306,6 @@ key_not_present(sftp_filehandle_t f, const char *priv) ...@@ -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 (!sftpc_read(sftp_state, f, off, (bufsz - bufpos > 1024) ? 1024 : bufsz - bufpos, &r)) {
if (sftp_state->err_code == SSH_FX_EOF) { if (sftp_state->err_code == SSH_FX_EOF) {
free(buf); free(buf);
free_sftp_str(r);
return true; return true;
} }
free(buf); free(buf);
...@@ -347,7 +346,6 @@ add_public_key(struct bbslist *bbs, char *priv) ...@@ -347,7 +346,6 @@ add_public_key(struct bbslist *bbs, char *priv)
bool added = false; bool added = false;
// TODO: Without this sleep, all is woe. // TODO: Without this sleep, all is woe.
//SLEEP(10);
while (!conn_api.input_thread_running) while (!conn_api.input_thread_running)
SLEEP(1); SLEEP(1);
if (!bbs->hidepopups) { if (!bbs->hidepopups) {
...@@ -584,7 +582,7 @@ ssh_connect(struct bbslist *bbs) ...@@ -584,7 +582,7 @@ ssh_connect(struct bbslist *bbs)
} }
} }
else { else {
if (!password[0]/* && ssh_context == -1*/) { if (!password[0] && ssh_context == -1) {
if (bbs->hidepopups) if (bbs->hidepopups)
init_uifc(false, false); init_uifc(false, false);
uifcinput("Password", MAX_PASSWD_LEN, password, K_PASSWORD, "Incorrect password. Try again."); uifcinput("Password", MAX_PASSWD_LEN, password, K_PASSWORD, "Incorrect password. Try again.");
...@@ -661,7 +659,38 @@ ssh_connect(struct bbslist *bbs) ...@@ -661,7 +659,38 @@ ssh_connect(struct bbslist *bbs)
uifc.pop(NULL); uifc.pop(NULL);
uifc.pop("Activating Session"); 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)) { if (cryptStatusError(status)) {
free(pubkey); free(pubkey);
error_popup(bbs, "activating session", status); error_popup(bbs, "activating session", status);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment