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);