mirror of https://github.com/wolfSSL/wolfssh.git
11441 lines
345 KiB
C
11441 lines
345 KiB
C
/* internal.c
|
|
*
|
|
* Copyright (C) 2014-2022 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfSSH.
|
|
*
|
|
* wolfSSH is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfSSH is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with wolfSSH. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
/*
|
|
* The internal module contains the private data and functions. The public
|
|
* API calls into this module to do the work of processing the connections.
|
|
*/
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <wolfssh/ssh.h>
|
|
#include <wolfssh/internal.h>
|
|
#include <wolfssh/log.h>
|
|
#include <wolfssl/wolfcrypt/asn.h>
|
|
#ifndef WOLFSSH_NO_DH
|
|
#include <wolfssl/wolfcrypt/dh.h>
|
|
#endif
|
|
#include <wolfssl/wolfcrypt/rsa.h>
|
|
#include <wolfssl/wolfcrypt/ecc.h>
|
|
#include <wolfssl/wolfcrypt/hmac.h>
|
|
#include <wolfssl/wolfcrypt/integer.h>
|
|
#include <wolfssl/wolfcrypt/signature.h>
|
|
|
|
#ifdef WOLFSSH_HAVE_LIBOQS
|
|
#include <oqs/kem.h>
|
|
#endif
|
|
|
|
#ifdef NO_INLINE
|
|
#include <wolfssh/misc.h>
|
|
#else
|
|
#define WOLFSSH_MISC_INCLUDED
|
|
#include "src/misc.c"
|
|
#endif
|
|
|
|
|
|
/*
|
|
Flags:
|
|
HAVE_WC_ECC_SET_RNG
|
|
Set by configure if wc_ecc_set_rng() discovered in wolfCrypt. Disables
|
|
use of the function if the flag isn't set. If using wolfCrypt v4.5.0 or
|
|
later, and not building with configure, set this flag.
|
|
default: off
|
|
WOLFSSH_NO_HMAC_SHA1
|
|
Set when HMAC or SHA1 are disabled. Set to disable HMAC-SHA1 support.
|
|
WOLFSSH_NO_HMAC_SHA1_96
|
|
Set when HMAC or SHA1 are disabled. Set to disable HMAC-SHA1-96 support.
|
|
WOLFSSH_NO_HMAC_SHA2_256
|
|
Set when HMAC or SHA2-256 are disabled. Set to disable HMAC-SHA2-256
|
|
support.
|
|
WOLFSSH_NO_DH_GROUP1_SHA1
|
|
Set when DH or SHA1 are disabled. Set to disable use of DH (Oakley 1) and
|
|
SHA1 support.
|
|
WOLFSSH_NO_DH_GROUP14_SHA1
|
|
Set when DH or SHA1 are disabled. Set to disable use of DH (Oakley 14) and
|
|
SHA1 support.
|
|
WOLFSSH_NO_DH_GEX_SHA256
|
|
Set when DH or SHA2-256 are disabled. Set to disable use of DH group
|
|
exchange and SHA2-256 support.
|
|
WOLFSSH_NO_ECDH_SHA2_NISTP256
|
|
Set when ECC or SHA2-256 are disabled. Set to disable use of ECDHE key
|
|
exchange with prime NISTP256.
|
|
WOLFSSH_NO_ECDH_SHA2_NISTP384
|
|
Set when ECC or SHA2-384 are disabled. Set to disable use of ECDHE key
|
|
exchange with prime NISTP384.
|
|
WOLFSSH_NO_ECDH_SHA2_NISTP521
|
|
Set when ECC or SHA2-512 are disabled. Set to disable use of ECDHE key
|
|
exchange with prime NISTP521.
|
|
WOLFSSH_NO_ECDH_SHA2_ED25519
|
|
Set when ED25519 or SHA2-256 are disabled. Set to disable use of ECDHE key
|
|
exchange with prime ED25519. (It just decodes the ID for output.)
|
|
WOLFSSH_NO_RSA
|
|
Set when RSA is disabled. Set to disable use of RSA server and user
|
|
authentication.
|
|
WOLFSSH_NO_SSH_RSA_SHA1
|
|
Set when RSA or SHA1 are disabled. Set to disable use of RSA server
|
|
authentication.
|
|
WOLFSSH_NO_ECDSA
|
|
Set when ECC is disabled. Set to disable use of ECDSA server and user
|
|
authentication.
|
|
WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
|
Set when ECC or SHA2-256 are disabled. Set to disable use of ECDSA server
|
|
authentication with prime NISTP256.
|
|
WOLFSSH_NO_ECDSA_SHA2_NISTP384
|
|
Set when ECC or SHA2-384 are disabled. Set to disable use of ECDSA server
|
|
authentication with prime NISTP384.
|
|
WOLFSSH_NO_ECDSA_SHA2_NISTP521
|
|
Set when ECC or SHA2-512 are disabled. Set to disable use of ECDSA server
|
|
authentication with prime NISTP521.
|
|
WOLFSSH_NO_SABER_LEVEL1_SHA256
|
|
Set when there is no liboqs integration. Set to disable use of post-quantum
|
|
SABER Level 1 KEM.
|
|
WOLFSSH_NO_AES_CBC
|
|
Set when AES or AES-CBC are disabled. Set to disable use of AES-CBC
|
|
encryption.
|
|
WOLFSSH_NO_AES_CTR
|
|
Set when AES or AES-CTR are disabled. Set to disable use of AES-CTR
|
|
encryption.
|
|
WOLFSSH_NO_AES_GCM
|
|
Set when AES or AES-GCM are disabled. Set to disable use of AES-GCM
|
|
encryption.
|
|
WOLFSSH_NO_AEAD
|
|
Set when AES-GCM is disabled. Set to disable use of AEAD ciphers for
|
|
encryption. Setting this will force all AEAD ciphers off.
|
|
WOLFSSH_NO_DH
|
|
Set when all DH algorithms are disabled. Set to disable use of all DH
|
|
algorithms for key agreement. Setting this will force all DH key agreement
|
|
algorithms off.
|
|
WOLFSSH_NO_ECDH
|
|
Set when all ECDH algorithms are disabled. Set to disable use of all ECDH
|
|
algorithms for key agreement. Setting this will force all ECDH key agreement
|
|
algorithms off.
|
|
*/
|
|
|
|
|
|
static const char sshProtoIdStr[] = "SSH-2.0-wolfSSHv"
|
|
LIBWOLFSSH_VERSION_STRING
|
|
"\r\n";
|
|
static const char OpenSSH[] = "SSH-2.0-OpenSSH";
|
|
|
|
|
|
const char* GetErrorString(int err)
|
|
{
|
|
(void)err;
|
|
|
|
#ifdef NO_WOLFSSH_STRINGS
|
|
return "No wolfSSH strings available";
|
|
#else
|
|
switch (err) {
|
|
case WS_ERROR:
|
|
return "general function failure";
|
|
|
|
case WS_BAD_ARGUMENT:
|
|
return "bad function argument";
|
|
|
|
case WS_MEMORY_E:
|
|
return "memory allocation failure";
|
|
|
|
case WS_BUFFER_E:
|
|
return "input/output buffer size error";
|
|
|
|
case WS_PARSE_E:
|
|
return "general parsing error";
|
|
|
|
case WS_NOT_COMPILED:
|
|
return "feature not compiled in";
|
|
|
|
case WS_OVERFLOW_E:
|
|
return "would overflow if continued failure";
|
|
|
|
case WS_BAD_USAGE:
|
|
return "bad example usage";
|
|
|
|
case WS_SOCKET_ERROR_E:
|
|
return "socket error";
|
|
|
|
case WS_WANT_READ:
|
|
return "I/O callback would read block error";
|
|
|
|
case WS_WANT_WRITE:
|
|
return "I/O callback would write block error";
|
|
|
|
case WS_RECV_OVERFLOW_E:
|
|
return "receive buffer overflow";
|
|
|
|
case WS_VERSION_E:
|
|
return "peer version unsupported";
|
|
|
|
case WS_SEND_OOB_READ_E:
|
|
return "attempted to read buffer out of bounds";
|
|
|
|
case WS_INPUT_CASE_E:
|
|
return "bad process input state, programming error";
|
|
|
|
case WS_BAD_FILETYPE_E:
|
|
return "bad filetype";
|
|
|
|
case WS_UNIMPLEMENTED_E:
|
|
return "feature not implemented";
|
|
|
|
case WS_RSA_E:
|
|
return "RSA buffer error";
|
|
|
|
case WS_BAD_FILE_E:
|
|
return "bad file";
|
|
|
|
case WS_INVALID_ALGO_ID:
|
|
return "invalid algorithm id";
|
|
|
|
case WS_DECRYPT_E:
|
|
return "decrypt error";
|
|
|
|
case WS_ENCRYPT_E:
|
|
return "encrypt error";
|
|
|
|
case WS_VERIFY_MAC_E:
|
|
return "verify mac error";
|
|
|
|
case WS_CREATE_MAC_E:
|
|
return "create mac error";
|
|
|
|
case WS_RESOURCE_E:
|
|
return "insufficient resources for new channel";
|
|
|
|
case WS_INVALID_CHANTYPE:
|
|
return "peer requested invalid channel type";
|
|
|
|
case WS_INVALID_CHANID:
|
|
return "peer requested invalid channel id";
|
|
|
|
case WS_INVALID_USERNAME:
|
|
return "invalid user name";
|
|
|
|
case WS_CRYPTO_FAILED:
|
|
return "crypto action failed";
|
|
|
|
case WS_INVALID_STATE_E:
|
|
return "invalid state";
|
|
|
|
case WS_EOF:
|
|
return "end of file";
|
|
|
|
case WS_REKEYING:
|
|
return "rekeying with peer";
|
|
|
|
case WS_INVALID_PRIME_CURVE:
|
|
return "invalid prime curve in ecc";
|
|
|
|
case WS_ECC_E:
|
|
return "ECDSA buffer error";
|
|
|
|
case WS_CHANOPEN_FAILED:
|
|
return "peer returned channel open failure";
|
|
|
|
case WS_CHANNEL_CLOSED:
|
|
return "channel closed";
|
|
|
|
case WS_INVALID_PATH_E:
|
|
return "invalid file or directory path";
|
|
|
|
case WS_SCP_CMD_E:
|
|
return "invalid scp command";
|
|
|
|
case WS_SCP_BAD_MSG_E:
|
|
return "invalid scp message received from peer";
|
|
|
|
case WS_SCP_PATH_LEN_E:
|
|
return "scp path length error";
|
|
|
|
case WS_SCP_TIMESTAMP_E:
|
|
return "scp timestamp message error";
|
|
|
|
case WS_SCP_DIR_STACK_EMPTY_E:
|
|
return "scp directory stack empty";
|
|
|
|
case WS_SCP_CONTINUE:
|
|
return "scp continue operation";
|
|
|
|
case WS_SCP_ABORT:
|
|
return "scp abort operation";
|
|
|
|
case WS_SCP_ENTER_DIR:
|
|
return "scp enter directory operation";
|
|
|
|
case WS_SCP_EXIT_DIR:
|
|
return "scp exit directory operation";
|
|
|
|
case WS_SCP_EXIT_DIR_FINAL:
|
|
return "scp final exit directory operation";
|
|
|
|
case WS_SCP_COMPLETE:
|
|
return "scp operation complete";
|
|
|
|
case WS_SCP_INIT:
|
|
return "scp operation verified";
|
|
|
|
case WS_MATCH_KEX_ALGO_E:
|
|
return "cannot match KEX algo with peer";
|
|
|
|
case WS_MATCH_KEY_ALGO_E:
|
|
return "cannot match key algo with peer";
|
|
|
|
case WS_MATCH_ENC_ALGO_E:
|
|
return "cannot match encrypt algo with peer";
|
|
|
|
case WS_MATCH_MAC_ALGO_E:
|
|
return "cannot match MAC algo with peer";
|
|
|
|
case WS_PERMISSIONS:
|
|
return "file permissions error";
|
|
|
|
case WS_SFTP_COMPLETE:
|
|
return "sftp connection established";
|
|
|
|
case WS_NEXT_ERROR:
|
|
return "Getting next value/state results in error";
|
|
|
|
case WS_CHAN_RXD:
|
|
return "Channel data received";
|
|
|
|
case WS_INVALID_EXTDATA:
|
|
return "invalid extended data type";
|
|
|
|
case WS_SFTP_BAD_REQ_ID:
|
|
return "sftp bad request id";
|
|
|
|
case WS_SFTP_BAD_REQ_TYPE:
|
|
return "sftp bad request response type";
|
|
|
|
case WS_SFTP_STATUS_NOT_OK:
|
|
return "sftp status not OK";
|
|
|
|
case WS_SFTP_FILE_DNE:
|
|
return "sftp file does not exist";
|
|
|
|
case WS_SIZE_ONLY:
|
|
return "Only getting the size of buffer needed";
|
|
|
|
case WS_CLOSE_FILE_E:
|
|
return "Unable to close local file";
|
|
|
|
case WS_PUBKEY_REJECTED_E:
|
|
return "server's public key is rejected";
|
|
|
|
case WS_EXTDATA:
|
|
return "Extended Data available to be read";
|
|
|
|
case WS_USER_AUTH_E:
|
|
return "User authentication error";
|
|
|
|
case WS_SSH_NULL_E:
|
|
return "ssh pointer was null";
|
|
|
|
case WS_SSH_CTX_NULL_E:
|
|
return "ssh ctx pointer was null";
|
|
|
|
case WS_CHANNEL_NOT_CONF:
|
|
return "channel open not confirmed";
|
|
|
|
case WS_CHANGE_AUTH_E:
|
|
return "changing auth type attempt";
|
|
|
|
case WS_WINDOW_FULL:
|
|
return "peer's channel window full";
|
|
|
|
case WS_MISSING_CALLBACK:
|
|
return "missing a callback function";
|
|
|
|
case WS_DH_SIZE_E:
|
|
return "DH prime group size larger than expected";
|
|
|
|
case WS_PUBKEY_SIG_MIN_E:
|
|
return "pubkey signature too small";
|
|
|
|
case WS_AGENT_NULL_E:
|
|
return "agent pointer was null";
|
|
|
|
case WS_AGENT_NO_KEY_E:
|
|
return "agent doesn't have requested key";
|
|
|
|
case WS_AGENT_CXN_FAIL:
|
|
return "agent connection failed";
|
|
|
|
case WS_SFTP_BAD_HEADER:
|
|
return "sftp bad header";
|
|
|
|
case WS_CERT_NO_SIGNER_E:
|
|
return "no signer certificate";
|
|
|
|
case WS_CERT_EXPIRED_E:
|
|
return "certificate expired";
|
|
|
|
case WS_CERT_REVOKED_E:
|
|
return "certificate revoked";
|
|
|
|
case WS_CERT_SIG_CONFIRM_E:
|
|
return "certificate signature fail";
|
|
|
|
case WS_CERT_OTHER_E:
|
|
return "other certificate error";
|
|
|
|
case WS_CERT_PROFILE_E:
|
|
return "certificate profile requirements error";
|
|
|
|
default:
|
|
return "Unknown error code";
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
static int wsHighwater(byte dir, void* ctx)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
(void)dir;
|
|
|
|
if (ctx) {
|
|
WOLFSSH* ssh = (WOLFSSH*)ctx;
|
|
|
|
WLOG(WS_LOG_DEBUG, "HIGHWATER MARK: (%u) %s",
|
|
wolfSSH_GetHighwater(ssh),
|
|
(dir == WOLFSSH_HWSIDE_RECEIVE) ? "receive" : "transmit");
|
|
|
|
ret = wolfSSH_TriggerKeyExchange(ssh);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* returns WS_SUCCESS on success */
|
|
static INLINE int HighwaterCheck(WOLFSSH* ssh, byte side)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (!ssh->highwaterFlag && ssh->highwaterMark &&
|
|
(ssh->txCount >= ssh->highwaterMark ||
|
|
ssh->rxCount >= ssh->highwaterMark)) {
|
|
|
|
WLOG(WS_LOG_DEBUG, "%s over high water mark",
|
|
(side == WOLFSSH_HWSIDE_TRANSMIT) ? "Transmit" : "Receive");
|
|
|
|
ssh->highwaterFlag = 1;
|
|
|
|
if (ssh->ctx->highwaterCb)
|
|
ret = ssh->ctx->highwaterCb(side, ssh->highwaterCtx);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
static HandshakeInfo* HandshakeInfoNew(void* heap)
|
|
{
|
|
HandshakeInfo* newHs;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering HandshakeInfoNew()");
|
|
newHs = (HandshakeInfo*)WMALLOC(sizeof(HandshakeInfo),
|
|
heap, DYNTYPE_HS);
|
|
if (newHs != NULL) {
|
|
WMEMSET(newHs, 0, sizeof(HandshakeInfo));
|
|
newHs->kexId = ID_NONE;
|
|
newHs->pubKeyId = ID_NONE;
|
|
newHs->sigId = ID_NONE;
|
|
newHs->encryptId = ID_NONE;
|
|
newHs->macId = ID_NONE;
|
|
newHs->blockSz = MIN_BLOCK_SZ;
|
|
newHs->hashId = WC_HASH_TYPE_NONE;
|
|
newHs->eSz = sizeof newHs->e;
|
|
newHs->xSz = sizeof newHs->x;
|
|
#ifndef WOLFSSH_NO_DH_GEX_SHA256
|
|
newHs->dhGexMinSz = WOLFSSH_DEFAULT_GEXDH_MIN;
|
|
newHs->dhGexPreferredSz = WOLFSSH_DEFAULT_GEXDH_PREFERRED;
|
|
newHs->dhGexMaxSz = WOLFSSH_DEFAULT_GEXDH_MAX;
|
|
#endif
|
|
}
|
|
|
|
return newHs;
|
|
}
|
|
|
|
|
|
static void HandshakeInfoFree(HandshakeInfo* hs, void* heap)
|
|
{
|
|
(void)heap;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering HandshakeInfoFree()");
|
|
if (hs) {
|
|
WFREE(hs->kexInit, heap, DYNTYPE_STRING);
|
|
#ifndef WOLFSSH_NO_DH
|
|
WFREE(hs->primeGroup, heap, DYNTYPE_MPINT);
|
|
WFREE(hs->generator, heap, DYNTYPE_MPINT);
|
|
#endif
|
|
if (hs->hashId != WC_HASH_TYPE_NONE)
|
|
wc_HashFree(&hs->hash, (enum wc_HashType)hs->hashId);
|
|
ForceZero(hs, sizeof(HandshakeInfo));
|
|
WFREE(hs, heap, DYNTYPE_HS);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef DEBUG_WOLFSSH
|
|
|
|
static const char cannedBanner[] =
|
|
"CANNED BANNER\r\n"
|
|
"This server is an example test server. "
|
|
"It should have its own banner, but\r\n"
|
|
"it is currently using a canned one in "
|
|
"the library. Be happy or not.\r\n";
|
|
static const word32 cannedBannerSz = sizeof(cannedBanner) - 1;
|
|
|
|
#endif /* DEBUG_WOLFSSH */
|
|
|
|
|
|
WOLFSSH_CTX* CtxInit(WOLFSSH_CTX* ctx, byte side, void* heap)
|
|
{
|
|
WLOG(WS_LOG_DEBUG, "Entering CtxInit()");
|
|
|
|
if (ctx == NULL)
|
|
return ctx;
|
|
|
|
WMEMSET(ctx, 0, sizeof(WOLFSSH_CTX));
|
|
|
|
if (heap)
|
|
ctx->heap = heap;
|
|
|
|
ctx->side = side;
|
|
#ifndef WOLFSSH_USER_IO
|
|
ctx->ioRecvCb = wsEmbedRecv;
|
|
ctx->ioSendCb = wsEmbedSend;
|
|
#endif /* WOLFSSH_USER_IO */
|
|
ctx->highwaterMark = DEFAULT_HIGHWATER_MARK;
|
|
ctx->highwaterCb = wsHighwater;
|
|
#if defined(WOLFSSH_SCP) && !defined(WOLFSSH_SCP_USER_CALLBACKS)
|
|
ctx->scpRecvCb = wsScpRecvCallback;
|
|
ctx->scpSendCb = wsScpSendCallback;
|
|
#endif /* WOLFSSH_SCP */
|
|
#ifdef DEBUG_WOLFSSH
|
|
ctx->banner = cannedBanner;
|
|
ctx->bannerSz = cannedBannerSz;
|
|
#endif /* DEBUG_WOLFSSH */
|
|
#ifdef WOLFSSH_CERTS
|
|
ctx->certMan = wolfSSH_CERTMAN_new(ctx->heap);
|
|
#endif /* WOLFSSH_CERTS */
|
|
ctx->windowSz = DEFAULT_WINDOW_SZ;
|
|
ctx->maxPacketSz = DEFAULT_MAX_PACKET_SZ;
|
|
|
|
return ctx;
|
|
}
|
|
|
|
|
|
void CtxResourceFree(WOLFSSH_CTX* ctx)
|
|
{
|
|
WLOG(WS_LOG_DEBUG, "Entering CtxResourceFree()");
|
|
|
|
if (ctx->privateKey) {
|
|
ForceZero(ctx->privateKey, ctx->privateKeySz);
|
|
WFREE(ctx->privateKey, ctx->heap, DYNTYPE_PRIVKEY);
|
|
}
|
|
#ifdef WOLFSSH_CERTS
|
|
if (ctx->certMan) {
|
|
wolfSSH_CERTMAN_free(ctx->certMan);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
WOLFSSH* SshInit(WOLFSSH* ssh, WOLFSSH_CTX* ctx)
|
|
{
|
|
#if defined(STM32F2) || defined(STM32F4) || defined(FREESCALE_MQX)
|
|
/* avoid name conflict in "stm32fnnnxx.h" */
|
|
#undef RNG
|
|
#define RNG WC_RNG
|
|
#endif
|
|
HandshakeInfo* handshake;
|
|
WC_RNG* rng;
|
|
void* heap;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SshInit()");
|
|
|
|
if (ssh == NULL || ctx == NULL)
|
|
return ssh;
|
|
heap = ctx->heap;
|
|
|
|
handshake = HandshakeInfoNew(heap);
|
|
rng = (WC_RNG*)WMALLOC(sizeof(WC_RNG), heap, DYNTYPE_RNG);
|
|
|
|
if (handshake == NULL || rng == NULL || wc_InitRng(rng) != 0) {
|
|
|
|
WLOG(WS_LOG_DEBUG, "SshInit: Cannot allocate memory.\n");
|
|
WFREE(handshake, heap, DYNTYPE_HS);
|
|
WFREE(rng, heap, DYNTYPE_RNG);
|
|
WFREE(ssh, heap, DYNTYPE_SSH);
|
|
return NULL;
|
|
}
|
|
|
|
WMEMSET(ssh, 0, sizeof(WOLFSSH)); /* default init to zeros */
|
|
|
|
ssh->ctx = ctx;
|
|
ssh->error = WS_SUCCESS;
|
|
#ifdef USE_WINDOWS_API
|
|
ssh->rfd = INVALID_SOCKET;
|
|
ssh->wfd = INVALID_SOCKET;
|
|
#else
|
|
ssh->rfd = -1; /* set to invalid */
|
|
ssh->wfd = -1; /* set to invalid */
|
|
#endif
|
|
ssh->ioReadCtx = &ssh->rfd; /* prevent invalid access if not correctly */
|
|
ssh->ioWriteCtx = &ssh->wfd; /* set */
|
|
ssh->highwaterMark = ctx->highwaterMark;
|
|
ssh->highwaterCtx = (void*)ssh;
|
|
ssh->reqSuccessCtx = (void*)ssh;
|
|
ssh->fs = NULL;
|
|
ssh->acceptState = ACCEPT_BEGIN;
|
|
ssh->clientState = CLIENT_BEGIN;
|
|
ssh->isKeying = 1;
|
|
ssh->authId = ID_USERAUTH_PUBLICKEY;
|
|
ssh->supportedAuth[0] = ID_USERAUTH_PUBLICKEY;
|
|
ssh->supportedAuth[1] = ID_USERAUTH_PASSWORD;
|
|
ssh->supportedAuth[2] = ID_NONE; /* ID_NONE is treated as empty slot */
|
|
ssh->nextChannel = DEFAULT_NEXT_CHANNEL;
|
|
ssh->blockSz = MIN_BLOCK_SZ;
|
|
ssh->encryptId = ID_NONE;
|
|
ssh->macId = ID_NONE;
|
|
ssh->peerBlockSz = MIN_BLOCK_SZ;
|
|
ssh->rng = rng;
|
|
ssh->kSz = sizeof(ssh->k);
|
|
ssh->handshake = handshake;
|
|
ssh->connectChannelId = WOLFSSH_SESSION_SHELL;
|
|
#ifdef WOLFSSH_SCP
|
|
ssh->scpRequestState = SCP_PARSE_COMMAND;
|
|
ssh->scpConfirmMsg = NULL;
|
|
ssh->scpConfirmMsgSz = 0;
|
|
ssh->scpRecvCtx = NULL;
|
|
#if !defined(WOLFSSH_SCP_USER_CALLBACKS) && !defined(NO_FILESYSTEM)
|
|
ssh->scpSendCtx = &(ssh->scpSendCbCtx);
|
|
#else
|
|
ssh->scpSendCtx = NULL;
|
|
#endif
|
|
ssh->scpFileBuffer = NULL;
|
|
ssh->scpFileBufferSz = 0;
|
|
ssh->scpFileName = NULL;
|
|
ssh->scpFileNameSz = 0;
|
|
ssh->scpTimestamp = 0;
|
|
ssh->scpATime = 0;
|
|
ssh->scpMTime = 0;
|
|
ssh->scpRequestType = WOLFSSH_SCP_SINGLE_FILE_REQUEST;
|
|
ssh->scpIsRecursive = 0;
|
|
ssh->scpDirection = WOLFSSH_SCP_DIR_NONE;
|
|
#endif
|
|
|
|
#ifdef WOLFSSH_SFTP
|
|
ssh->sftpState = SFTP_BEGIN;
|
|
#endif
|
|
|
|
#ifdef WOLFSSH_AGENT
|
|
ssh->agentEnabled = ctx->agentEnabled;
|
|
#endif
|
|
|
|
if (BufferInit(&ssh->inputBuffer, 0, ctx->heap) != WS_SUCCESS ||
|
|
BufferInit(&ssh->outputBuffer, 0, ctx->heap) != WS_SUCCESS ||
|
|
BufferInit(&ssh->extDataBuffer, 0, ctx->heap) != WS_SUCCESS) {
|
|
|
|
wolfSSH_free(ssh);
|
|
ssh = NULL;
|
|
}
|
|
|
|
return ssh;
|
|
}
|
|
|
|
|
|
void SshResourceFree(WOLFSSH* ssh, void* heap)
|
|
{
|
|
/* when ssh holds resources, free here */
|
|
(void)heap;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering sshResourceFree()");
|
|
|
|
ShrinkBuffer(&ssh->inputBuffer, 1);
|
|
ShrinkBuffer(&ssh->outputBuffer, 1);
|
|
ShrinkBuffer(&ssh->extDataBuffer, 1);
|
|
ForceZero(ssh->k, ssh->kSz);
|
|
HandshakeInfoFree(ssh->handshake, heap);
|
|
ForceZero(&ssh->keys, sizeof(Keys));
|
|
ForceZero(&ssh->peerKeys, sizeof(Keys));
|
|
if (ssh->rng) {
|
|
wc_FreeRng(ssh->rng);
|
|
WFREE(ssh->rng, heap, DYNTYPE_RNG);
|
|
}
|
|
if (ssh->userName) {
|
|
WFREE(ssh->userName, heap, DYNTYPE_STRING);
|
|
}
|
|
if (ssh->peerProtoId) {
|
|
WFREE(ssh->peerProtoId, heap, DYNTYPE_STRING);
|
|
}
|
|
if (ssh->channelList) {
|
|
WOLFSSH_CHANNEL* cur = ssh->channelList;
|
|
WOLFSSH_CHANNEL* next;
|
|
while (cur) {
|
|
next = cur->next;
|
|
ChannelDelete(cur, heap);
|
|
cur = next;
|
|
}
|
|
}
|
|
wc_AesFree(&ssh->encryptCipher.aes);
|
|
wc_AesFree(&ssh->decryptCipher.aes);
|
|
|
|
#ifdef WOLFSSH_SCP
|
|
if (ssh->scpConfirmMsg) {
|
|
WFREE(ssh->scpConfirmMsg, ssh->ctx->heap, DYNTYPE_STRING);
|
|
ssh->scpConfirmMsg = NULL;
|
|
ssh->scpConfirmMsgSz = 0;
|
|
}
|
|
if (ssh->scpFileBuffer) {
|
|
ForceZero(ssh->scpFileBuffer, ssh->scpFileBufferSz);
|
|
WFREE(ssh->scpFileBuffer, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
ssh->scpFileBuffer = NULL;
|
|
ssh->scpFileBufferSz = 0;
|
|
}
|
|
if (ssh->scpFileName) {
|
|
WFREE(ssh->scpFileName, ssh->ctx->heap, DYNTYPE_STRING);
|
|
ssh->scpFileName = NULL;
|
|
ssh->scpFileNameSz = 0;
|
|
}
|
|
if (ssh->scpRecvMsg) {
|
|
WFREE(ssh->scpRecvMsg, ssh->heap, DYNTYPE_STRING);
|
|
ssh->scpRecvMsg = NULL;
|
|
ssh->scpRecvMsgSz = 0;
|
|
}
|
|
#ifdef WOLFSSL_NUCLEUS
|
|
WFREE(ssh->scpBasePathDynamic, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
ssh->scpBasePathDynamic = NULL;
|
|
ssh->scpBasePathSz = 0;
|
|
#endif
|
|
#endif
|
|
#ifdef WOLFSSH_SFTP
|
|
if (ssh->sftpDefaultPath) {
|
|
WFREE(ssh->sftpDefaultPath, ssh->ctx->heap, DYNTYPE_STRING);
|
|
ssh->sftpDefaultPath = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
union wolfSSH_key {
|
|
#ifndef WOLFSSH_NO_RSA
|
|
RsaKey rsa;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
ecc_key ecc;
|
|
#endif
|
|
};
|
|
|
|
int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx,
|
|
const byte* in, word32 inSz,
|
|
int format, int type)
|
|
{
|
|
int dynamicType = 0;
|
|
int wcType;
|
|
int ret;
|
|
void* heap = NULL;
|
|
byte* der;
|
|
word32 derSz, scratch = 0;
|
|
union wolfSSH_key *key_ptr = NULL;
|
|
|
|
(void)wcType;
|
|
(void)dynamicType;
|
|
(void)heap;
|
|
|
|
if (ctx == NULL || in == NULL || inSz == 0)
|
|
return WS_BAD_ARGUMENT;
|
|
|
|
if (format != WOLFSSH_FORMAT_ASN1 && format != WOLFSSH_FORMAT_PEM &&
|
|
format != WOLFSSH_FORMAT_RAW)
|
|
return WS_BAD_FILETYPE_E;
|
|
|
|
if (type == BUFTYPE_CA) {
|
|
dynamicType = DYNTYPE_CA;
|
|
wcType = CA_TYPE;
|
|
}
|
|
else if (type == BUFTYPE_CERT) {
|
|
dynamicType = DYNTYPE_CERT;
|
|
wcType = CERT_TYPE;
|
|
}
|
|
else if (type == BUFTYPE_PRIVKEY) {
|
|
dynamicType = DYNTYPE_PRIVKEY;
|
|
wcType = PRIVATEKEY_TYPE;
|
|
}
|
|
else
|
|
return WS_BAD_ARGUMENT;
|
|
|
|
heap = ctx->heap;
|
|
|
|
if (format == WOLFSSH_FORMAT_ASN1 || format == WOLFSSH_FORMAT_RAW) {
|
|
if (in[0] != 0x30)
|
|
return WS_BAD_FILETYPE_E;
|
|
der = (byte*)WMALLOC(inSz, heap, dynamicType);
|
|
if (der == NULL)
|
|
return WS_MEMORY_E;
|
|
WMEMCPY(der, in, inSz);
|
|
derSz = inSz;
|
|
}
|
|
#ifdef WOLFSSH_CERTS
|
|
else if (format == WOLFSSH_FORMAT_PEM) {
|
|
/* The der size will be smaller than the pem size. */
|
|
der = (byte*)WMALLOC(inSz, heap, dynamicType);
|
|
if (der == NULL)
|
|
return WS_MEMORY_E;
|
|
|
|
derSz = wc_CertPemToDer(in, inSz, der, inSz, wcType);
|
|
if (derSz < 0) {
|
|
WFREE(der, heap, dynamicType);
|
|
return WS_BAD_FILE_E;
|
|
}
|
|
}
|
|
#endif /* WOLFSSH_CERTS */
|
|
else {
|
|
return WS_UNIMPLEMENTED_E;
|
|
}
|
|
|
|
/* Maybe decrypt */
|
|
|
|
if (type == BUFTYPE_PRIVKEY) {
|
|
if (ctx->privateKey) {
|
|
ForceZero(ctx->privateKey, ctx->privateKeySz);
|
|
WFREE(ctx->privateKey, heap, dynamicType);
|
|
}
|
|
ctx->privateKey = der;
|
|
ctx->privateKeySz = derSz;
|
|
ctx->useEcc = 0;
|
|
}
|
|
#ifdef WOLFSSH_CERTS
|
|
else if (type == BUFTYPE_CERT) {
|
|
if (ctx->cert != NULL)
|
|
WFREE(ctx->cert, heap, 0);
|
|
ctx->cert = der;
|
|
ctx->certSz = derSz;
|
|
ctx->useCert = 1;
|
|
}
|
|
else if (type == BUFTYPE_CA) {
|
|
if (ctx->certMan != NULL) {
|
|
ret = wolfSSH_CERTMAN_LoadRootCA_buffer(ctx->certMan, der, derSz);
|
|
}
|
|
WFREE(der, heap, dynamicType);
|
|
}
|
|
#endif /* WOLFSSH_CERTS */
|
|
else {
|
|
WFREE(der, heap, dynamicType);
|
|
return WS_UNIMPLEMENTED_E;
|
|
}
|
|
|
|
if (type == BUFTYPE_PRIVKEY && format != WOLFSSH_FORMAT_RAW) {
|
|
key_ptr = (union wolfSSH_key*)WMALLOC(sizeof(union wolfSSH_key), heap,
|
|
dynamicType);
|
|
if (key_ptr == NULL) {
|
|
WFREE(der, heap, dynamicType);
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
/* Check RSA key */
|
|
#ifndef WOLFSSH_NO_RSA
|
|
scratch = 0;
|
|
if (wc_InitRsaKey(&key_ptr->rsa, NULL) < 0) {
|
|
ret = WS_RSA_E;
|
|
goto end;
|
|
}
|
|
|
|
ret = wc_RsaPrivateKeyDecode(der, &scratch, &key_ptr->rsa, derSz);
|
|
wc_FreeRsaKey(&key_ptr->rsa);
|
|
|
|
if (ret < 0) {
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
/* Couldn't decode as RSA key. Try decoding as ECC key. */
|
|
scratch = 0;
|
|
if (wc_ecc_init_ex(&key_ptr->ecc, ctx->heap, INVALID_DEVID) != 0) {
|
|
ret = WS_ECC_E;
|
|
goto end;
|
|
}
|
|
|
|
ret = wc_EccPrivateKeyDecode(ctx->privateKey, &scratch,
|
|
&key_ptr->ecc, ctx->privateKeySz);
|
|
if (ret == 0) {
|
|
int curveId = wc_ecc_get_curve_id(key_ptr->ecc.idx);
|
|
if (curveId == ECC_SECP256R1 ||
|
|
curveId == ECC_SECP384R1 ||
|
|
curveId == ECC_SECP521R1) {
|
|
|
|
ctx->useEcc = curveId;
|
|
}
|
|
else
|
|
ret = WS_BAD_FILE_E;
|
|
}
|
|
wc_ecc_free(&key_ptr->ecc);
|
|
if (ret != 0) {
|
|
ret = WS_BAD_FILE_E;
|
|
goto end;
|
|
}
|
|
#endif /* ! WOLFSSH_NO_ECDSA */
|
|
|
|
#ifndef WOLFSSH_NO_RSA
|
|
}
|
|
#endif
|
|
}
|
|
ret = WS_SUCCESS;
|
|
end:
|
|
if (key_ptr)
|
|
WFREE(key_ptr, heap, dynamicType);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int GenerateKey(byte hashId, byte keyId,
|
|
byte* key, word32 keySz,
|
|
const byte* k, word32 kSz,
|
|
const byte* h, word32 hSz,
|
|
const byte* sessionId, word32 sessionIdSz)
|
|
{
|
|
word32 blocks, remainder;
|
|
wc_HashAlg hash;
|
|
enum wc_HashType enmhashId = (enum wc_HashType)hashId;
|
|
byte kPad = 0;
|
|
byte pad = 0;
|
|
byte kSzFlat[LENGTH_SZ];
|
|
int digestSz;
|
|
int ret;
|
|
|
|
if (key == NULL || keySz == 0 ||
|
|
k == NULL || kSz == 0 ||
|
|
h == NULL || hSz == 0 ||
|
|
sessionId == NULL || sessionIdSz == 0) {
|
|
|
|
WLOG(WS_LOG_DEBUG, "GK: bad argument");
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
digestSz = wc_HashGetDigestSize(enmhashId);
|
|
if (digestSz <= 0) {
|
|
WLOG(WS_LOG_DEBUG, "GK: bad hash ID");
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
if (k[0] & 0x80) kPad = 1;
|
|
c32toa(kSz + kPad, kSzFlat);
|
|
|
|
blocks = keySz / digestSz;
|
|
remainder = keySz % digestSz;
|
|
|
|
ret = wc_HashInit(&hash, enmhashId);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashUpdate(&hash, enmhashId, kSzFlat, LENGTH_SZ);
|
|
if (ret == WS_SUCCESS && kPad)
|
|
ret = wc_HashUpdate(&hash, enmhashId, &pad, 1);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashUpdate(&hash, enmhashId, k, kSz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashUpdate(&hash, enmhashId, h, hSz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashUpdate(&hash, enmhashId, &keyId, sizeof(keyId));
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashUpdate(&hash, enmhashId, sessionId, sessionIdSz);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (blocks == 0) {
|
|
if (remainder > 0) {
|
|
byte lastBlock[WC_MAX_DIGEST_SIZE];
|
|
ret = wc_HashFinal(&hash, enmhashId, lastBlock);
|
|
if (ret == WS_SUCCESS)
|
|
WMEMCPY(key, lastBlock, remainder);
|
|
}
|
|
}
|
|
else {
|
|
word32 runningKeySz, curBlock;
|
|
|
|
runningKeySz = digestSz;
|
|
ret = wc_HashFinal(&hash, enmhashId, key);
|
|
|
|
for (curBlock = 1; curBlock < blocks; curBlock++) {
|
|
ret = wc_HashInit(&hash, enmhashId);
|
|
if (ret != WS_SUCCESS) break;
|
|
ret = wc_HashUpdate(&hash, enmhashId, kSzFlat, LENGTH_SZ);
|
|
if (ret != WS_SUCCESS) break;
|
|
if (kPad)
|
|
ret = wc_HashUpdate(&hash, enmhashId, &pad, 1);
|
|
if (ret != WS_SUCCESS) break;
|
|
ret = wc_HashUpdate(&hash, enmhashId, k, kSz);
|
|
if (ret != WS_SUCCESS) break;
|
|
ret = wc_HashUpdate(&hash, enmhashId, h, hSz);
|
|
if (ret != WS_SUCCESS) break;
|
|
ret = wc_HashUpdate(&hash, enmhashId, key, runningKeySz);
|
|
if (ret != WS_SUCCESS) break;
|
|
ret = wc_HashFinal(&hash, enmhashId, key + runningKeySz);
|
|
if (ret != WS_SUCCESS) break;
|
|
runningKeySz += digestSz;
|
|
}
|
|
|
|
if (remainder > 0) {
|
|
byte lastBlock[WC_MAX_DIGEST_SIZE];
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashInit(&hash, enmhashId);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashUpdate(&hash, enmhashId, kSzFlat, LENGTH_SZ);
|
|
if (ret == WS_SUCCESS && kPad)
|
|
ret = wc_HashUpdate(&hash, enmhashId, &pad, 1);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashUpdate(&hash, enmhashId, k, kSz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashUpdate(&hash, enmhashId, h, hSz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashUpdate(&hash, enmhashId, key, runningKeySz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashFinal(&hash, enmhashId, lastBlock);
|
|
if (ret == WS_SUCCESS)
|
|
WMEMCPY(key + runningKeySz, lastBlock, remainder);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ret != WS_SUCCESS)
|
|
ret = WS_CRYPTO_FAILED;
|
|
wc_HashFree(&hash, enmhashId);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int GenerateKeys(WOLFSSH* ssh, byte hashId)
|
|
{
|
|
Keys* cK = NULL;
|
|
Keys* sK = NULL;
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
else {
|
|
if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) {
|
|
cK = &ssh->handshake->peerKeys;
|
|
sK = &ssh->handshake->keys;
|
|
}
|
|
else {
|
|
cK = &ssh->handshake->keys;
|
|
sK = &ssh->handshake->peerKeys;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GenerateKey(hashId, 'A',
|
|
cK->iv, cK->ivSz,
|
|
ssh->k, ssh->kSz, ssh->h, ssh->hSz,
|
|
ssh->sessionId, ssh->sessionIdSz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = GenerateKey(hashId, 'B',
|
|
sK->iv, sK->ivSz,
|
|
ssh->k, ssh->kSz, ssh->h, ssh->hSz,
|
|
ssh->sessionId, ssh->sessionIdSz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = GenerateKey(hashId, 'C',
|
|
cK->encKey, cK->encKeySz,
|
|
ssh->k, ssh->kSz, ssh->h, ssh->hSz,
|
|
ssh->sessionId, ssh->sessionIdSz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = GenerateKey(hashId, 'D',
|
|
sK->encKey, sK->encKeySz,
|
|
ssh->k, ssh->kSz, ssh->h, ssh->hSz,
|
|
ssh->sessionId, ssh->sessionIdSz);
|
|
if (ret == WS_SUCCESS) {
|
|
if (!ssh->handshake->aeadMode) {
|
|
ret = GenerateKey(hashId, 'E',
|
|
cK->macKey, cK->macKeySz,
|
|
ssh->k, ssh->kSz, ssh->h, ssh->hSz,
|
|
ssh->sessionId, ssh->sessionIdSz);
|
|
if (ret == WS_SUCCESS) {
|
|
ret = GenerateKey(hashId, 'F',
|
|
sK->macKey, sK->macKeySz,
|
|
ssh->k, ssh->kSz, ssh->h, ssh->hSz,
|
|
ssh->sessionId, ssh->sessionIdSz);
|
|
}
|
|
}
|
|
}
|
|
#ifdef SHOW_SECRETS
|
|
if (ret == WS_SUCCESS) {
|
|
printf("\n** Showing Secrets **\nK:\n");
|
|
DumpOctetString(ssh->k, ssh->kSz);
|
|
printf("H:\n");
|
|
DumpOctetString(ssh->h, ssh->hSz);
|
|
printf("Session ID:\n");
|
|
DumpOctetString(ssh->sessionId, ssh->sessionIdSz);
|
|
printf("A:\n");
|
|
DumpOctetString(cK->iv, cK->ivSz);
|
|
printf("B:\n");
|
|
DumpOctetString(sK->iv, sK->ivSz);
|
|
printf("C:\n");
|
|
DumpOctetString(cK->encKey, cK->encKeySz);
|
|
printf("D:\n");
|
|
DumpOctetString(sK->encKey, sK->encKeySz);
|
|
printf("E:\n");
|
|
DumpOctetString(cK->macKey, cK->macKeySz);
|
|
printf("F:\n");
|
|
DumpOctetString(sK->macKey, sK->macKeySz);
|
|
printf("\n");
|
|
}
|
|
#endif /* SHOW_SECRETS */
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
byte id;
|
|
const char* name;
|
|
} NameIdPair;
|
|
|
|
|
|
static const NameIdPair NameIdMap[] = {
|
|
{ ID_NONE, "none" },
|
|
|
|
/* Encryption IDs */
|
|
#ifndef WOLFSSH_NO_AES_CBC
|
|
{ ID_AES128_CBC, "aes128-cbc" },
|
|
{ ID_AES192_CBC, "aes192-cbc" },
|
|
{ ID_AES256_CBC, "aes256-cbc" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_AES_CTR
|
|
{ ID_AES128_CTR, "aes128-ctr" },
|
|
{ ID_AES192_CTR, "aes192-ctr" },
|
|
{ ID_AES256_CTR, "aes256-ctr" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_AES_GCM
|
|
{ ID_AES128_GCM, "aes128-gcm@openssh.com" },
|
|
{ ID_AES192_GCM, "aes192-gcm@openssh.com" },
|
|
{ ID_AES256_GCM, "aes256-gcm@openssh.com" },
|
|
#endif
|
|
|
|
/* Integrity IDs */
|
|
#ifndef WOLFSSH_NO_HMAC_SHA1
|
|
{ ID_HMAC_SHA1, "hmac-sha1" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_HMAC_SHA1_96
|
|
{ ID_HMAC_SHA1_96, "hmac-sha1-96" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_HMAC_SHA2_256
|
|
{ ID_HMAC_SHA2_256, "hmac-sha2-256" },
|
|
#endif
|
|
|
|
/* Key Exchange IDs */
|
|
#ifndef WOLFSSH_NO_DH_GROUP1_SHA1
|
|
{ ID_DH_GROUP1_SHA1, "diffie-hellman-group1-sha1" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_DH_GROUP14_SHA1
|
|
{ ID_DH_GROUP14_SHA1, "diffie-hellman-group14-sha1" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_DH_GEX_SHA256
|
|
{ ID_DH_GEX_SHA256, "diffie-hellman-group-exchange-sha256" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256
|
|
{ ID_ECDH_SHA2_NISTP256, "ecdh-sha2-nistp256" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP384
|
|
{ ID_ECDH_SHA2_NISTP384, "ecdh-sha2-nistp384" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521
|
|
{ ID_ECDH_SHA2_NISTP521, "ecdh-sha2-nistp521" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_ED25519
|
|
{ ID_ECDH_SHA2_ED25519, "curve25519-sha256" },
|
|
{ ID_ECDH_SHA2_ED25519_LIBSSH, "curve25519-sha256@libssh.org" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_DH_GEX_SHA256
|
|
{ ID_DH_GROUP14_SHA256, "diffie-hellman-group14-sha256" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256
|
|
/* We use lightsaber here to achieve interop with OQS's fork. */
|
|
{ ID_SABER_LEVEL1_SHA256, "saber-lightsaber-sha256" },
|
|
#endif
|
|
/* Public Key IDs */
|
|
#ifndef WOLFSSH_NO_SSH_RSA_SHA1
|
|
{ ID_SSH_RSA, "ssh-rsa" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
|
{ ID_ECDSA_SHA2_NISTP256, "ecdsa-sha2-nistp256" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
|
|
{ ID_ECDSA_SHA2_NISTP384, "ecdsa-sha2-nistp384" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
|
|
{ ID_ECDSA_SHA2_NISTP521, "ecdsa-sha2-nistp521" },
|
|
#endif
|
|
#ifdef WOLFSSH_CERTS
|
|
#ifndef WOLFSSH_NO_SSH_RSA_SHA1
|
|
{ ID_X509V3_SSH_RSA, "x509v3-ssh-rsa" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
|
{ ID_X509V3_ECDSA_SHA2_NISTP256, "x509v3-ecdsa-sha2-nistp256" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
|
|
{ ID_X509V3_ECDSA_SHA2_NISTP384, "x509v3-ecdsa-sha2-nistp384" },
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
|
|
{ ID_X509V3_ECDSA_SHA2_NISTP521, "x509v3-ecdsa-sha2-nistp521" },
|
|
#endif
|
|
#endif /* WOLFSSH_CERTS */
|
|
|
|
/* Service IDs */
|
|
{ ID_SERVICE_USERAUTH, "ssh-userauth" },
|
|
{ ID_SERVICE_CONNECTION, "ssh-connection" },
|
|
|
|
/* UserAuth IDs */
|
|
{ ID_USERAUTH_PASSWORD, "password" },
|
|
{ ID_USERAUTH_PUBLICKEY, "publickey" },
|
|
|
|
/* Channel Type IDs */
|
|
{ ID_CHANTYPE_SESSION, "session" },
|
|
#ifdef WOLFSSH_FWD
|
|
{ ID_CHANTYPE_TCPIP_FORWARD, "forwarded-tcpip" },
|
|
{ ID_CHANTYPE_TCPIP_DIRECT, "direct-tcpip" },
|
|
#endif /* WOLFSSH_FWD */
|
|
#ifdef WOLFSSH_AGENT
|
|
{ ID_CHANTYPE_AUTH_AGENT, "auth-agent@openssh.com" },
|
|
#endif /* WOLFSSH_AGENT */
|
|
|
|
/* Global Request IDs */
|
|
#ifdef WOLFSSH_FWD
|
|
{ ID_GLOBREQ_TCPIP_FWD, "tcpip-forward" },
|
|
{ ID_GLOBREQ_TCPIP_FWD_CANCEL, "cancel-tcpip-forward" },
|
|
#endif /* WOLFSSH_FWD */
|
|
};
|
|
|
|
|
|
byte NameToId(const char* name, word32 nameSz)
|
|
{
|
|
byte id = ID_UNKNOWN;
|
|
word32 i;
|
|
|
|
for (i = 0; i < (sizeof(NameIdMap)/sizeof(NameIdPair)); i++) {
|
|
if (nameSz == (word32)WSTRLEN(NameIdMap[i].name) &&
|
|
XMEMCMP(name, NameIdMap[i].name, nameSz) == 0) {
|
|
|
|
id = NameIdMap[i].id;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
|
|
const char* IdToName(byte id)
|
|
{
|
|
const char* name = "unknown";
|
|
word32 i;
|
|
|
|
for (i = 0; i < (sizeof(NameIdMap)/sizeof(NameIdPair)); i++) {
|
|
if (NameIdMap[i].id == id) {
|
|
name = NameIdMap[i].name;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
|
|
WOLFSSH_CHANNEL* ChannelNew(WOLFSSH* ssh, byte channelType,
|
|
word32 initialWindowSz, word32 maxPacketSz)
|
|
{
|
|
WOLFSSH_CHANNEL* newChannel = NULL;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering ChannelNew()");
|
|
if (ssh == NULL || ssh->ctx == NULL) {
|
|
WLOG(WS_LOG_DEBUG, "Trying to create new channel without ssh or ctx");
|
|
}
|
|
else {
|
|
void* heap = ssh->ctx->heap;
|
|
|
|
newChannel = (WOLFSSH_CHANNEL*)WMALLOC(sizeof(WOLFSSH_CHANNEL),
|
|
heap, DYNTYPE_CHANNEL);
|
|
if (newChannel != NULL)
|
|
{
|
|
byte* buffer;
|
|
|
|
buffer = (byte*)WMALLOC(initialWindowSz, heap, DYNTYPE_BUFFER);
|
|
if (buffer != NULL) {
|
|
WMEMSET(newChannel, 0, sizeof(WOLFSSH_CHANNEL));
|
|
newChannel->ssh = ssh;
|
|
newChannel->channelType = channelType;
|
|
newChannel->channel = ssh->nextChannel++;
|
|
WLOG(WS_LOG_DEBUG, "New channel id = %u", newChannel->channel);
|
|
newChannel->windowSz = initialWindowSz;
|
|
newChannel->maxPacketSz = maxPacketSz;
|
|
/*
|
|
* In the context of the channel input buffer, the buffer is
|
|
* a fixed size. The property length will be the insert point
|
|
* for new received data. The property idx will be the pull
|
|
* point for the data.
|
|
*/
|
|
newChannel->inputBuffer.heap = heap;
|
|
newChannel->inputBuffer.buffer = buffer;
|
|
newChannel->inputBuffer.bufferSz = initialWindowSz;
|
|
newChannel->inputBuffer.dynamicFlag = 1;
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "Unable to allocate new channel's buffer");
|
|
WFREE(newChannel, heap, DYNTYPE_CHANNEL);
|
|
newChannel = NULL;
|
|
}
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "Unable to allocate new channel");
|
|
}
|
|
}
|
|
|
|
WLOG(WS_LOG_INFO, "Leaving ChannelNew(), ret = %p", newChannel);
|
|
|
|
return newChannel;
|
|
}
|
|
|
|
|
|
void ChannelDelete(WOLFSSH_CHANNEL* channel, void* heap)
|
|
{
|
|
(void)heap;
|
|
|
|
if (channel) {
|
|
#ifdef WOLFSSH_FWD
|
|
if (channel->host)
|
|
WFREE(channel->host, heap, DYNTYPE_STRING);
|
|
if (channel->origin)
|
|
WFREE(channel->origin, heap, DYNTYPE_STRING);
|
|
#endif /* WOLFSSH_FWD */
|
|
WFREE(channel->inputBuffer.buffer,
|
|
channel->inputBuffer.heap, DYNTYPE_BUFFER);
|
|
if (channel->command)
|
|
WFREE(channel->command, heap, DYNTYPE_STRING);
|
|
WFREE(channel, heap, DYNTYPE_CHANNEL);
|
|
}
|
|
}
|
|
|
|
|
|
WOLFSSH_CHANNEL* ChannelFind(WOLFSSH* ssh, word32 channel, byte peer)
|
|
{
|
|
WOLFSSH_CHANNEL* findChannel = NULL;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering ChannelFind(): %s %u",
|
|
peer ? "peer" : "self", channel);
|
|
|
|
if (ssh == NULL) {
|
|
WLOG(WS_LOG_DEBUG, "Null ssh, not looking for channel");
|
|
}
|
|
else {
|
|
WOLFSSH_CHANNEL* list = ssh->channelList;
|
|
word32 listSz = ssh->channelListSz;
|
|
|
|
while (list && listSz) {
|
|
if (channel == ((peer == WS_CHANNEL_ID_PEER) ?
|
|
list->peerChannel : list->channel)) {
|
|
findChannel = list;
|
|
break;
|
|
}
|
|
list = list->next;
|
|
listSz--;
|
|
}
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving ChannelFind(): %p", findChannel);
|
|
|
|
return findChannel;
|
|
}
|
|
|
|
|
|
int ChannelUpdatePeer(WOLFSSH_CHANNEL* channel, word32 peerChannelId,
|
|
word32 peerInitialWindowSz, word32 peerMaxPacketSz)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (channel == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
else {
|
|
channel->peerChannel = peerChannelId;
|
|
channel->peerWindowSz = peerInitialWindowSz;
|
|
channel->peerMaxPacketSz = peerMaxPacketSz;
|
|
channel->openConfirmed = 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifdef WOLFSSH_FWD
|
|
int ChannelUpdateForward(WOLFSSH_CHANNEL* channel,
|
|
const char* host, word32 hostPort,
|
|
const char* origin, word32 originPort,
|
|
int isDirect)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
char* hostCopy = NULL;
|
|
char* originCopy = NULL;
|
|
word32 hostSz;
|
|
word32 originSz;
|
|
|
|
if (channel == NULL || host == NULL || origin == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
else {
|
|
void* heap = channel->ssh->ctx->heap;
|
|
|
|
hostSz = (word32)WSTRLEN(host) + 1;
|
|
originSz = (word32)WSTRLEN(origin) + 1;
|
|
hostCopy = (char*)WMALLOC(hostSz, heap, DYNTYPE_STRING);
|
|
originCopy = (char*)WMALLOC(originSz, heap, DYNTYPE_STRING);
|
|
if (hostCopy == NULL || originCopy == NULL) {
|
|
WFREE(hostCopy, heap, DYNTYPE_STRING);
|
|
WFREE(originCopy, heap, DYNTYPE_STRING);
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WSTRNCPY(hostCopy, host, hostSz);
|
|
WSTRNCPY(originCopy, origin, originSz);
|
|
|
|
/* delete any existing host and origin in the channel */
|
|
if (channel->host)
|
|
WFREE(channel->host, heap, DYNTYPE_STRING);
|
|
if (channel->origin)
|
|
WFREE(channel->origin, heap, DYNTYPE_STRING);
|
|
|
|
channel->host = hostCopy;
|
|
channel->hostPort = hostPort;
|
|
channel->origin = originCopy;
|
|
channel->originPort = originPort;
|
|
channel->isDirect = isDirect;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSH_FWD */
|
|
|
|
|
|
int ChannelAppend(WOLFSSH* ssh, WOLFSSH_CHANNEL* channel)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering ChannelAppend()");
|
|
|
|
if (ssh == NULL || channel == NULL) {
|
|
ret = WS_BAD_ARGUMENT;
|
|
WLOG(WS_LOG_DEBUG, "Leaving ChannelAppend(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (ssh->channelList == NULL) {
|
|
ssh->channelList = channel;
|
|
ssh->channelListSz = 1;
|
|
}
|
|
else {
|
|
WOLFSSH_CHANNEL* cur = ssh->channelList;
|
|
while (cur->next != NULL)
|
|
cur = cur->next;
|
|
cur->next = channel;
|
|
ssh->channelListSz++;
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving ChannelAppend(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int ChannelRemove(WOLFSSH* ssh, word32 channel, byte peer)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
WOLFSSH_CHANNEL* list;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering ChannelRemove()");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
list = ssh->channelList;
|
|
if (list == NULL)
|
|
ret = WS_INVALID_CHANID;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WOLFSSH_CHANNEL* prev = NULL;
|
|
word32 listSz = ssh->channelListSz;
|
|
|
|
while (list && listSz) {
|
|
if (channel == ((peer == WS_CHANNEL_ID_PEER) ?
|
|
list->peerChannel : list->channel)) {
|
|
if (prev == NULL)
|
|
ssh->channelList = list->next;
|
|
else
|
|
prev->next = list->next;
|
|
ChannelDelete(list, ssh->ctx->heap);
|
|
ssh->channelListSz--;
|
|
|
|
break;
|
|
}
|
|
prev = list;
|
|
list = list->next;
|
|
listSz--;
|
|
}
|
|
|
|
if (listSz == 0)
|
|
ret = WS_INVALID_CHANID;
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving ChannelRemove(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int ChannelPutData(WOLFSSH_CHANNEL* channel, byte* data, word32 dataSz)
|
|
{
|
|
Buffer* inBuf;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering ChannelPutData()");
|
|
|
|
if (channel == NULL || data == NULL)
|
|
return WS_BAD_ARGUMENT;
|
|
|
|
inBuf = &channel->inputBuffer;
|
|
|
|
/* sanity check the current state to see if is too much data */
|
|
if (dataSz > channel->windowSz) {
|
|
WLOG(WS_LOG_ERROR, "Internal state error, too much data");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
if (inBuf->length < inBuf->bufferSz &&
|
|
inBuf->length + dataSz <= inBuf->bufferSz) {
|
|
|
|
WMEMCPY(inBuf->buffer + inBuf->length, data, dataSz);
|
|
inBuf->length += dataSz;
|
|
|
|
WLOG(WS_LOG_INFO, " dataSz = %u", dataSz);
|
|
WLOG(WS_LOG_INFO, " windowSz = %u", channel->windowSz);
|
|
channel->windowSz -= dataSz;
|
|
WLOG(WS_LOG_INFO, " update windowSz = %u", channel->windowSz);
|
|
}
|
|
else {
|
|
return WS_RECV_OVERFLOW_E;
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
int BufferInit(Buffer* buffer, word32 size, void* heap)
|
|
{
|
|
if (buffer == NULL)
|
|
return WS_BAD_ARGUMENT;
|
|
|
|
if (size <= STATIC_BUFFER_LEN)
|
|
size = STATIC_BUFFER_LEN;
|
|
|
|
WMEMSET(buffer, 0, sizeof(Buffer));
|
|
buffer->heap = heap;
|
|
buffer->bufferSz = size;
|
|
if (size > STATIC_BUFFER_LEN) {
|
|
buffer->buffer = (byte*)WMALLOC(size, heap, DYNTYPE_BUFFER);
|
|
if (buffer->buffer == NULL)
|
|
return WS_MEMORY_E;
|
|
buffer->dynamicFlag = 1;
|
|
}
|
|
else
|
|
buffer->buffer = buffer->staticBuffer;
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
int GrowBuffer(Buffer* buf, word32 sz, word32 usedSz)
|
|
{
|
|
#if 0
|
|
WLOG(WS_LOG_DEBUG, "GB: buf = %p", buf);
|
|
WLOG(WS_LOG_DEBUG, "GB: sz = %d", sz);
|
|
WLOG(WS_LOG_DEBUG, "GB: usedSz = %d", usedSz);
|
|
#endif
|
|
/* New buffer will end up being sz+usedSz long
|
|
* empty space at the head of the buffer will be compressed */
|
|
if (buf != NULL) {
|
|
word32 newSz = sz + usedSz;
|
|
/*WLOG(WS_LOG_DEBUG, "GB: newSz = %d", newSz);*/
|
|
|
|
if (newSz > buf->bufferSz) {
|
|
byte* newBuffer = (byte*)WMALLOC(newSz,
|
|
buf->heap, DYNTYPE_BUFFER);
|
|
|
|
if (newBuffer == NULL) {
|
|
WLOG(WS_LOG_ERROR, "Not enough memory left to grow buffer");
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
/*WLOG(WS_LOG_DEBUG, "GB: resizing buffer");*/
|
|
if (buf->length > 0 && usedSz > 0)
|
|
WMEMCPY(newBuffer, buf->buffer + buf->idx, usedSz);
|
|
|
|
if (!buf->dynamicFlag)
|
|
buf->dynamicFlag = 1;
|
|
else
|
|
WFREE(buf->buffer, buf->heap, DYNTYPE_BUFFER);
|
|
|
|
buf->buffer = newBuffer;
|
|
buf->bufferSz = newSz;
|
|
buf->length = usedSz;
|
|
buf->idx = 0;
|
|
}
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
void ShrinkBuffer(Buffer* buf, int forcedFree)
|
|
{
|
|
WLOG(WS_LOG_DEBUG, "Entering ShrinkBuffer()");
|
|
|
|
if (buf != NULL) {
|
|
word32 usedSz = buf->length - buf->idx;
|
|
|
|
WLOG(WS_LOG_DEBUG, "SB: usedSz = %u, forcedFree = %u",
|
|
usedSz, forcedFree);
|
|
|
|
if (!forcedFree && usedSz > STATIC_BUFFER_LEN)
|
|
return;
|
|
|
|
if (!forcedFree && usedSz) {
|
|
WLOG(WS_LOG_DEBUG, "SB: shifting down");
|
|
WMEMCPY(buf->staticBuffer, buf->buffer + buf->idx, usedSz);
|
|
}
|
|
|
|
if (buf->dynamicFlag) {
|
|
WLOG(WS_LOG_DEBUG, "SB: releasing dynamic buffer");
|
|
WFREE(buf->buffer, buf->heap, DYNTYPE_BUFFER);
|
|
}
|
|
buf->dynamicFlag = 0;
|
|
buf->buffer = buf->staticBuffer;
|
|
buf->bufferSz = STATIC_BUFFER_LEN;
|
|
buf->length = forcedFree ? 0 : usedSz;
|
|
buf->idx = 0;
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving ShrinkBuffer()");
|
|
}
|
|
|
|
|
|
static int Receive(WOLFSSH* ssh, byte* buf, word32 sz)
|
|
{
|
|
int recvd;
|
|
|
|
if (ssh->ctx->ioRecvCb == NULL) {
|
|
WLOG(WS_LOG_DEBUG, "Your IO Recv callback is null, please set");
|
|
return -1;
|
|
}
|
|
|
|
retry:
|
|
recvd = ssh->ctx->ioRecvCb(ssh, buf, sz, ssh->ioReadCtx);
|
|
WLOG(WS_LOG_DEBUG, "Receive: recvd = %d", recvd);
|
|
if (recvd < 0)
|
|
switch (recvd) {
|
|
case WS_CBIO_ERR_GENERAL: /* general/unknown error */
|
|
return -1;
|
|
|
|
case WS_CBIO_ERR_WANT_READ: /* want read, would block */
|
|
return WS_WANT_READ;
|
|
|
|
case WS_CBIO_ERR_CONN_RST: /* connection reset */
|
|
ssh->connReset = 1;
|
|
return -1;
|
|
|
|
case WS_CBIO_ERR_ISR: /* interrupt */
|
|
goto retry;
|
|
|
|
case WS_CBIO_ERR_CONN_CLOSE: /* peer closed connection */
|
|
ssh->isClosed = 1;
|
|
return -1;
|
|
|
|
case WS_CBIO_ERR_TIMEOUT:
|
|
return -1;
|
|
|
|
default:
|
|
return recvd;
|
|
}
|
|
|
|
return recvd;
|
|
}
|
|
|
|
|
|
static int GetInputText(WOLFSSH* ssh, byte** pEol)
|
|
{
|
|
int gotLine = 0;
|
|
int inSz = 255;
|
|
int in;
|
|
char *eol;
|
|
|
|
if (GrowBuffer(&ssh->inputBuffer, inSz, 0) < 0)
|
|
return WS_MEMORY_E;
|
|
|
|
do {
|
|
in = Receive(ssh,
|
|
ssh->inputBuffer.buffer + ssh->inputBuffer.length, inSz);
|
|
|
|
if (in == -1)
|
|
return WS_SOCKET_ERROR_E;
|
|
|
|
if (in == WS_WANT_READ)
|
|
return WS_WANT_READ;
|
|
|
|
if (in > inSz)
|
|
return WS_RECV_OVERFLOW_E;
|
|
|
|
ssh->inputBuffer.length += in;
|
|
inSz -= in;
|
|
|
|
eol = WSTRNSTR((const char*)ssh->inputBuffer.buffer, "\r\n",
|
|
ssh->inputBuffer.length);
|
|
|
|
/* section 4.2 in RFC 4253 states that can be lenient on the CR for
|
|
* interop with older or undocumented versions of SSH */
|
|
if (!eol) {
|
|
WLOG(WS_LOG_DEBUG, "Checking for old version of protocol exchange");
|
|
eol = WSTRNSTR((const char*)ssh->inputBuffer.buffer, "\n",
|
|
ssh->inputBuffer.length);
|
|
}
|
|
|
|
if (eol)
|
|
gotLine = 1;
|
|
|
|
} while (!gotLine && inSz);
|
|
|
|
if (pEol)
|
|
*pEol = (byte*)eol;
|
|
|
|
return (gotLine ? WS_SUCCESS : WS_VERSION_E);
|
|
}
|
|
|
|
|
|
/* returns WS_SUCCESS on success */
|
|
int wolfSSH_SendPacket(WOLFSSH* ssh)
|
|
{
|
|
WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SendPacket()");
|
|
|
|
if (ssh->ctx->ioSendCb == NULL) {
|
|
WLOG(WS_LOG_DEBUG, "Your IO Send callback is null, please set");
|
|
return WS_SOCKET_ERROR_E;
|
|
}
|
|
|
|
while (ssh->outputBuffer.length > 0) {
|
|
int sent;
|
|
|
|
/* sanity check on amount requested to be sent */
|
|
if (ssh->outputBuffer.idx + ssh->outputBuffer.length >
|
|
ssh->outputBuffer.bufferSz) {
|
|
WLOG(WS_LOG_ERROR, "Bad buffer state");
|
|
return WS_BUFFER_E;
|
|
}
|
|
|
|
sent = ssh->ctx->ioSendCb(ssh,
|
|
ssh->outputBuffer.buffer + ssh->outputBuffer.idx,
|
|
ssh->outputBuffer.length, ssh->ioWriteCtx);
|
|
|
|
if (sent < 0) {
|
|
switch (sent) {
|
|
case WS_CBIO_ERR_WANT_WRITE: /* want write, would block */
|
|
ssh->error = WS_WANT_WRITE;
|
|
return WS_WANT_WRITE;
|
|
|
|
case WS_CBIO_ERR_CONN_RST: /* connection reset */
|
|
ssh->connReset = 1;
|
|
break;
|
|
|
|
case WS_CBIO_ERR_CONN_CLOSE: /* peer closed connection */
|
|
ssh->isClosed = 1;
|
|
break;
|
|
|
|
case WS_CBIO_ERR_GENERAL:
|
|
ShrinkBuffer(&ssh->outputBuffer, 1);
|
|
}
|
|
return WS_SOCKET_ERROR_E;
|
|
}
|
|
|
|
if ((word32)sent > ssh->outputBuffer.length) {
|
|
WLOG(WS_LOG_DEBUG, "wolfSSH_SendPacket() out of bounds read");
|
|
return WS_SEND_OOB_READ_E;
|
|
}
|
|
|
|
ssh->outputBuffer.idx += sent;
|
|
ssh->outputBuffer.length -= sent;
|
|
}
|
|
|
|
ssh->outputBuffer.idx = 0;
|
|
ssh->outputBuffer.plainSz = 0;
|
|
|
|
WLOG(WS_LOG_DEBUG, "SB: Shrinking output buffer");
|
|
ShrinkBuffer(&ssh->outputBuffer, 0);
|
|
return HighwaterCheck(ssh, WOLFSSH_HWSIDE_TRANSMIT);
|
|
}
|
|
|
|
|
|
static int GetInputData(WOLFSSH* ssh, word32 size)
|
|
{
|
|
int in;
|
|
int inSz;
|
|
int maxLength;
|
|
int usedLength;
|
|
|
|
/* check max input length */
|
|
usedLength = ssh->inputBuffer.length - ssh->inputBuffer.idx;
|
|
maxLength = ssh->inputBuffer.bufferSz - usedLength;
|
|
inSz = (int)(size - usedLength); /* from last partial read */
|
|
#if 0
|
|
WLOG(WS_LOG_DEBUG, "GID: size = %u", size);
|
|
WLOG(WS_LOG_DEBUG, "GID: usedLength = %d", usedLength);
|
|
WLOG(WS_LOG_DEBUG, "GID: maxLength = %d", maxLength);
|
|
WLOG(WS_LOG_DEBUG, "GID: inSz = %d", inSz);
|
|
#endif
|
|
/*
|
|
* usedLength - how much untouched data is in the buffer
|
|
* maxLength - how much empty space is in the buffer
|
|
* inSz - difference between requested data and empty space in the buffer
|
|
* how much more we need to allocate
|
|
*/
|
|
|
|
if (inSz <= 0)
|
|
return WS_SUCCESS;
|
|
|
|
/*
|
|
* If we need more space than there is left in the buffer grow buffer.
|
|
* Growing the buffer also compresses empty space at the head of the
|
|
* buffer and resets idx to 0.
|
|
*/
|
|
if (inSz > maxLength) {
|
|
if (GrowBuffer(&ssh->inputBuffer, size, usedLength) < 0) {
|
|
ssh->error = WS_MEMORY_E;
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
}
|
|
|
|
/* Put buffer data at start if not there */
|
|
/* Compress the buffer if needed, i.e. buffer idx is non-zero */
|
|
if (usedLength > 0 && ssh->inputBuffer.idx != 0) {
|
|
WMEMMOVE(ssh->inputBuffer.buffer,
|
|
ssh->inputBuffer.buffer + ssh->inputBuffer.idx,
|
|
usedLength);
|
|
}
|
|
|
|
/* remove processed data */
|
|
ssh->inputBuffer.idx = 0;
|
|
ssh->inputBuffer.length = usedLength;
|
|
|
|
/* read data from network */
|
|
do {
|
|
in = Receive(ssh,
|
|
ssh->inputBuffer.buffer + ssh->inputBuffer.length, inSz);
|
|
if (in == -1) {
|
|
ssh->error = WS_SOCKET_ERROR_E;
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
if (in == WS_WANT_READ) {
|
|
ssh->error = WS_WANT_READ;
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
if (in > inSz) {
|
|
ssh->error = WS_RECV_OVERFLOW_E;
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
if (in >= 0) {
|
|
ssh->inputBuffer.length += in;
|
|
inSz -= in;
|
|
}
|
|
else {
|
|
/* all other unexpected negative values is a failure case */
|
|
ssh->error = WS_FATAL_ERROR;
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
} while (ssh->inputBuffer.length < size);
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
int GetBoolean(byte* v, byte* buf, word32 len, word32* idx)
|
|
{
|
|
int result = WS_BUFFER_E;
|
|
|
|
if (*idx < len) {
|
|
*v = buf[*idx];
|
|
*idx += BOOLEAN_SZ;
|
|
result = WS_SUCCESS;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
int GetUint32(word32* v, const byte* buf, word32 len, word32* idx)
|
|
{
|
|
int result = WS_BUFFER_E;
|
|
|
|
if (*idx < len && UINT32_SZ <= len - *idx) {
|
|
ato32(buf + *idx, v);
|
|
*idx += UINT32_SZ;
|
|
result = WS_SUCCESS;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
int GetSize(word32* v, const byte* buf, word32 len, word32* idx)
|
|
{
|
|
int result;
|
|
|
|
result = GetUint32(v, buf, len, idx);
|
|
if (result == WS_SUCCESS) {
|
|
if (*v > len - *idx) {
|
|
result = WS_BUFFER_E;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* Gets the size of the mpint, and puts the pointer to the start of
|
|
* buf's number into *mpint. This function does not copy. */
|
|
int GetMpint(word32* mpintSz, byte** mpint, byte* buf, word32 len, word32* idx)
|
|
{
|
|
int result;
|
|
|
|
result = GetUint32(mpintSz, buf, len, idx);
|
|
|
|
if (result == WS_SUCCESS) {
|
|
result = WS_BUFFER_E;
|
|
|
|
if (*idx < len && *mpintSz <= len - *idx) {
|
|
*mpint = buf + *idx;
|
|
*idx += *mpintSz;
|
|
result = WS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* Gets the size of a string, copies it as much of it as will fit in
|
|
* the provided buffer, and terminates it with a NULL. */
|
|
int GetString(char* s, word32* sSz, byte* buf, word32 len, word32 *idx)
|
|
{
|
|
int result;
|
|
word32 strSz;
|
|
|
|
result = GetUint32(&strSz, buf, len, idx);
|
|
|
|
if (result == WS_SUCCESS) {
|
|
result = WS_BUFFER_E;
|
|
|
|
/* This allows 0 length string to be decoded */
|
|
if (*idx <= len && strSz <= len - *idx) {
|
|
*sSz = (strSz >= *sSz) ? *sSz - 1 : strSz; /* -1 for null char */
|
|
WMEMCPY(s, buf + *idx, *sSz);
|
|
*idx += strSz;
|
|
s[*sSz] = 0;
|
|
result = WS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* Gets the size of a string, allocates memory to hold it plus a NULL, then
|
|
* copies it into the allocated buffer, and terminates it with a NULL. */
|
|
int GetStringAlloc(void* heap, char** s, byte* buf, word32 len, word32 *idx)
|
|
{
|
|
int result;
|
|
char* str;
|
|
word32 strSz;
|
|
|
|
result = GetUint32(&strSz, buf, len, idx);
|
|
|
|
if (result == WS_SUCCESS) {
|
|
if (*idx >= len || strSz > len - *idx)
|
|
return WS_BUFFER_E;
|
|
str = (char*)WMALLOC(strSz + 1, heap, DYNTYPE_STRING);
|
|
if (str == NULL)
|
|
return WS_MEMORY_E;
|
|
WMEMCPY(str, buf + *idx, strSz);
|
|
*idx += strSz;
|
|
str[strSz] = '\0';
|
|
|
|
if (*s != NULL)
|
|
WFREE(*s, heap, DYNTYPE_STRING);
|
|
*s = str;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* Gets the size of the string, and puts the pointer to the start of
|
|
* buf's string into *str. This function does not copy. */
|
|
int GetStringRef(word32* strSz, byte** str, byte* buf, word32 len, word32* idx)
|
|
{
|
|
int result;
|
|
|
|
result = GetUint32(strSz, buf, len, idx);
|
|
|
|
if (result == WS_SUCCESS) {
|
|
result = WS_BUFFER_E;
|
|
|
|
if (*idx < len && *strSz <= len - *idx) {
|
|
*str = buf + *idx;
|
|
*idx += *strSz;
|
|
result = WS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static int GetNameList(byte* idList, word32* idListSz,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
byte idListIdx;
|
|
word32 nameListSz, nameListIdx;
|
|
word32 begin;
|
|
byte* name;
|
|
word32 nameSz;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering GetNameList()");
|
|
|
|
if (idList == NULL || idListSz == NULL ||
|
|
buf == NULL || len == 0 || idx == NULL) {
|
|
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
/*
|
|
* This iterates across a name list and finds names that end in either the
|
|
* comma delimeter or with the end of the list.
|
|
*/
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
if (begin >= len || begin + 4 >= len)
|
|
ret = WS_BUFFER_E;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&nameListSz, buf, len, &begin);
|
|
|
|
/* The strings we want are now in the bounds of the message, and the
|
|
* length of the list. Find the commas, or end of list, and then decode
|
|
* the values. */
|
|
if (ret == WS_SUCCESS) {
|
|
name = buf + begin;
|
|
nameSz = 0;
|
|
nameListIdx = 0;
|
|
idListIdx = 0;
|
|
|
|
while (nameListIdx < nameListSz) {
|
|
nameListIdx++;
|
|
|
|
if (nameListIdx == nameListSz)
|
|
nameSz++;
|
|
|
|
if (nameListIdx + begin >= len)
|
|
return WS_BUFFER_E;
|
|
|
|
if (nameListIdx == nameListSz || name[nameSz] == ',') {
|
|
byte id;
|
|
|
|
id = NameToId((char*)name, nameSz);
|
|
{
|
|
const char* displayName = IdToName(id);
|
|
if (displayName) {
|
|
WLOG(WS_LOG_DEBUG, "DNL: name ID = %s", displayName);
|
|
}
|
|
}
|
|
if (id != ID_UNKNOWN || idListIdx == 0) {
|
|
/* Intentionally save the first one if unknown. This helps
|
|
* skipping the KexDhInit if the client sends the wrong one
|
|
* as a guess. */
|
|
if (idListIdx >= *idListSz) {
|
|
WLOG(WS_LOG_ERROR, "No more space left for names");
|
|
return WS_BUFFER_E;
|
|
}
|
|
idList[idListIdx++] = id;
|
|
}
|
|
|
|
name += 1 + nameSz;
|
|
nameSz = 0;
|
|
}
|
|
else
|
|
nameSz++;
|
|
}
|
|
|
|
begin += nameListSz;
|
|
*idListSz = idListIdx;
|
|
*idx = begin;
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving GetNameList(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
static const byte cannedEncAlgo[] = {
|
|
#ifndef WOLFSSH_NO_AES_GCM
|
|
ID_AES256_GCM,
|
|
ID_AES192_GCM,
|
|
ID_AES128_GCM,
|
|
#endif
|
|
#ifndef WOLFSSH_NO_AES_CTR
|
|
ID_AES256_CTR,
|
|
ID_AES192_CTR,
|
|
ID_AES128_CTR,
|
|
#endif
|
|
#ifndef WOLFSSH_NO_AES_CBC
|
|
ID_AES256_CBC,
|
|
ID_AES192_CBC,
|
|
ID_AES128_CBC,
|
|
#endif
|
|
};
|
|
|
|
static const byte cannedMacAlgo[] = {
|
|
#ifndef WOLFSSH_NO_HMAC_SHA2_256
|
|
ID_HMAC_SHA2_256,
|
|
#endif
|
|
#ifndef WOLFSSH_NO_HMAC_SHA1_96
|
|
ID_HMAC_SHA1_96,
|
|
#endif
|
|
#ifndef WOLFSSH_NO_HMAC_SHA1
|
|
ID_HMAC_SHA1,
|
|
#endif
|
|
};
|
|
|
|
static const byte cannedKeyAlgoClient[] = {
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
|
|
ID_ECDSA_SHA2_NISTP521,
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
|
|
ID_ECDSA_SHA2_NISTP384,
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
|
ID_ECDSA_SHA2_NISTP256,
|
|
#endif
|
|
#ifndef WOLFSSH_NO_SSH_RSA_SHA1
|
|
ID_SSH_RSA,
|
|
#endif
|
|
#ifdef WOLFSSH_CERTS
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
|
|
ID_X509V3_ECDSA_SHA2_NISTP521,
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
|
|
ID_X509V3_ECDSA_SHA2_NISTP384,
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
|
ID_X509V3_ECDSA_SHA2_NISTP256,
|
|
#endif
|
|
#ifndef WOLFSSH_NO_SSH_RSA_SHA1
|
|
ID_X509V3_SSH_RSA,
|
|
#endif
|
|
#endif
|
|
};
|
|
|
|
#ifndef WOLFSSH_NO_SSH_RSA_SHA1
|
|
static const byte cannedKeyAlgoRsa[] = {ID_SSH_RSA};
|
|
static const word32 cannedKeyAlgoRsaSz = sizeof(cannedKeyAlgoRsa);
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
|
static const byte cannedKeyAlgoEcc256[] = {ID_ECDSA_SHA2_NISTP256};
|
|
static const word32 cannedKeyAlgoEcc256Sz = sizeof(cannedKeyAlgoEcc256);
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
|
|
static const byte cannedKeyAlgoEcc384[] = {ID_ECDSA_SHA2_NISTP384};
|
|
static const word32 cannedKeyAlgoEcc384Sz = sizeof(cannedKeyAlgoEcc384);
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
|
|
static const byte cannedKeyAlgoEcc521[] = {ID_ECDSA_SHA2_NISTP521};
|
|
static const word32 cannedKeyAlgoEcc521Sz = sizeof(cannedKeyAlgoEcc521);
|
|
#endif
|
|
#ifdef WOLFSSH_CERTS
|
|
#ifndef WOLFSSH_NO_SSH_RSA_SHA1
|
|
static const byte cannedKeyAlgoX509Rsa[] = {ID_SSH_RSA};
|
|
static const word32 cannedKeyAlgoX509RsaSz = sizeof(cannedKeyAlgoX509Rsa);
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
|
static const byte cannedKeyAlgoX509Ecc256[] = {ID_ECDSA_SHA2_NISTP256};
|
|
static const word32 cannedKeyAlgoX509Ecc256Sz =
|
|
sizeof(cannedKeyAlgoX509Ecc256);
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
|
|
static const byte cannedKeyAlgoX509Ecc384[] = {ID_ECDSA_SHA2_NISTP384};
|
|
static const word32 cannedKeyAlgoX509Ecc384Sz =
|
|
sizeof(cannedKeyAlgoX509Ecc384);
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
|
|
static const byte cannedKeyAlgoX509Ecc521[] = {ID_ECDSA_SHA2_NISTP521};
|
|
static const word32 cannedKeyAlgoX509Ecc521Sz =
|
|
sizeof(cannedKeyAlgoX509Ecc521);
|
|
#endif
|
|
#endif /* WOLFSSH_CERTS */
|
|
|
|
|
|
static const byte cannedKexAlgo[] = {
|
|
#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256
|
|
ID_SABER_LEVEL1_SHA256,
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521
|
|
ID_ECDH_SHA2_NISTP521,
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP384
|
|
ID_ECDH_SHA2_NISTP384,
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256
|
|
ID_ECDH_SHA2_NISTP256,
|
|
#endif
|
|
#ifndef WOLFSSH_NO_DH_GEX_SHA256
|
|
ID_DH_GEX_SHA256,
|
|
#endif
|
|
#ifndef WOLFSSH_NO_DH_GROUP14_SHA1
|
|
ID_DH_GROUP14_SHA1,
|
|
#endif
|
|
#ifndef WOLFSSH_NO_DH_GROUP1_SHA1
|
|
ID_DH_GROUP1_SHA1,
|
|
#endif
|
|
};
|
|
|
|
static const word32 cannedEncAlgoSz = sizeof(cannedEncAlgo);
|
|
static const word32 cannedMacAlgoSz = sizeof(cannedMacAlgo);
|
|
static const word32 cannedKeyAlgoClientSz = sizeof(cannedKeyAlgoClient);
|
|
static const word32 cannedKexAlgoSz = sizeof(cannedKexAlgo);
|
|
|
|
|
|
static byte MatchIdLists(int side, const byte* left, word32 leftSz,
|
|
const byte* right, word32 rightSz)
|
|
{
|
|
word32 i, j;
|
|
|
|
/* When matching on the client, swap left and right. Left should be
|
|
* the client's list and right should be the server's list. */
|
|
if (side == WOLFSSH_ENDPOINT_CLIENT) {
|
|
const byte* swap = left;
|
|
word32 swapSz = leftSz;
|
|
|
|
left = right;
|
|
right = swap;
|
|
leftSz = rightSz;
|
|
rightSz = swapSz;
|
|
}
|
|
|
|
if (left != NULL && leftSz > 0 && right != NULL && rightSz > 0) {
|
|
for (i = 0; i < leftSz; i++) {
|
|
for (j = 0; j < rightSz; j++) {
|
|
if (left[i] == right[j]) {
|
|
#if 0
|
|
WLOG(WS_LOG_DEBUG, "MID: matched %s", IdToName(left[i]));
|
|
#endif
|
|
return left[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ID_UNKNOWN;
|
|
}
|
|
|
|
|
|
static INLINE byte BlockSzForId(byte id)
|
|
{
|
|
switch (id) {
|
|
#ifndef WOLFSSH_NO_AES_CBC
|
|
case ID_AES128_CBC:
|
|
case ID_AES192_CBC:
|
|
case ID_AES256_CBC:
|
|
return AES_BLOCK_SIZE;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_AES_CTR
|
|
case ID_AES128_CTR:
|
|
case ID_AES192_CTR:
|
|
case ID_AES256_CTR:
|
|
return AES_BLOCK_SIZE;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_AES_GCM
|
|
case ID_AES128_GCM:
|
|
case ID_AES192_GCM:
|
|
case ID_AES256_GCM:
|
|
return AES_BLOCK_SIZE;
|
|
#endif
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static INLINE byte MacSzForId(byte id)
|
|
{
|
|
switch (id) {
|
|
#ifndef WOLFSSH_NO_HMAC_SHA1
|
|
case ID_HMAC_SHA1:
|
|
return WC_SHA_DIGEST_SIZE;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_HMAC_SHA1_96
|
|
case ID_HMAC_SHA1_96:
|
|
return SHA1_96_SZ;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_HMAC_SHA2_256
|
|
case ID_HMAC_SHA2_256:
|
|
return WC_SHA256_DIGEST_SIZE;
|
|
#endif
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static INLINE byte KeySzForId(byte id)
|
|
{
|
|
switch (id) {
|
|
#ifndef WOLFSSH_NO_HMAC_SHA1
|
|
case ID_HMAC_SHA1:
|
|
return WC_SHA_DIGEST_SIZE;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_HMAC_SHA1_96
|
|
case ID_HMAC_SHA1_96:
|
|
return WC_SHA_DIGEST_SIZE;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_HMAC_SHA2_256
|
|
case ID_HMAC_SHA2_256:
|
|
return WC_SHA256_DIGEST_SIZE;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_AES_CBC
|
|
case ID_AES128_CBC:
|
|
return AES_128_KEY_SIZE;
|
|
case ID_AES192_CBC:
|
|
return AES_192_KEY_SIZE;
|
|
case ID_AES256_CBC:
|
|
return AES_256_KEY_SIZE;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_AES_CTR
|
|
case ID_AES128_CTR:
|
|
return AES_128_KEY_SIZE;
|
|
case ID_AES192_CTR:
|
|
return AES_192_KEY_SIZE;
|
|
case ID_AES256_CTR:
|
|
return AES_256_KEY_SIZE;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_AES_GCM
|
|
case ID_AES128_GCM:
|
|
return AES_128_KEY_SIZE;
|
|
case ID_AES192_GCM:
|
|
return AES_192_KEY_SIZE;
|
|
case ID_AES256_GCM:
|
|
return AES_256_KEY_SIZE;
|
|
#endif
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static INLINE enum wc_HashType HashForId(byte id)
|
|
{
|
|
switch (id) {
|
|
|
|
/* SHA1 */
|
|
#ifndef WOLFSSH_NO_DH_GROUP1_SHA1
|
|
case ID_DH_GROUP1_SHA1:
|
|
return WC_HASH_TYPE_SHA;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_DH_GROUP14_SHA1
|
|
case ID_DH_GROUP14_SHA1:
|
|
return WC_HASH_TYPE_SHA;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_SSH_RSA_SHA1
|
|
case ID_SSH_RSA:
|
|
return WC_HASH_TYPE_SHA;
|
|
#endif
|
|
|
|
/* SHA2-256 */
|
|
#ifndef WOLFSSH_NO_DH_GEX_SHA256
|
|
case ID_DH_GEX_SHA256:
|
|
return WC_HASH_TYPE_SHA256;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256
|
|
case ID_ECDH_SHA2_NISTP256:
|
|
return WC_HASH_TYPE_SHA256;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
|
case ID_ECDSA_SHA2_NISTP256:
|
|
#ifdef WOLFSSH_CERTS
|
|
case ID_X509V3_ECDSA_SHA2_NISTP256:
|
|
#endif
|
|
return WC_HASH_TYPE_SHA256;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256
|
|
case ID_SABER_LEVEL1_SHA256:
|
|
return WC_HASH_TYPE_SHA256;
|
|
#endif
|
|
|
|
/* SHA2-384 */
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP384
|
|
case ID_ECDH_SHA2_NISTP384:
|
|
return WC_HASH_TYPE_SHA384;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
|
|
case ID_ECDSA_SHA2_NISTP384:
|
|
#ifdef WOLFSSH_CERTS
|
|
case ID_X509V3_ECDSA_SHA2_NISTP384:
|
|
#endif
|
|
return WC_HASH_TYPE_SHA384;
|
|
#endif
|
|
|
|
/* SHA2-512 */
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521
|
|
case ID_ECDH_SHA2_NISTP521:
|
|
return WC_HASH_TYPE_SHA512;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
|
|
case ID_ECDSA_SHA2_NISTP521:
|
|
#ifdef WOLFSSH_CERTS
|
|
case ID_X509V3_ECDSA_SHA2_NISTP521:
|
|
#endif
|
|
return WC_HASH_TYPE_SHA512;
|
|
#endif
|
|
default:
|
|
return WC_HASH_TYPE_NONE;
|
|
}
|
|
}
|
|
|
|
|
|
#if !defined(WOLFSSH_NO_ECDSA) || !defined(WOLFSSH_NO_ECDH)
|
|
static INLINE int wcPrimeForId(byte id)
|
|
{
|
|
switch (id) {
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256
|
|
case ID_ECDH_SHA2_NISTP256:
|
|
return ECC_SECP256R1;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
|
case ID_ECDSA_SHA2_NISTP256:
|
|
return ECC_SECP256R1;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP384
|
|
case ID_ECDH_SHA2_NISTP384:
|
|
return ECC_SECP384R1;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
|
|
case ID_ECDSA_SHA2_NISTP384:
|
|
return ECC_SECP384R1;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521
|
|
case ID_ECDH_SHA2_NISTP521:
|
|
return ECC_SECP521R1;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
|
|
case ID_ECDSA_SHA2_NISTP521:
|
|
return ECC_SECP521R1;
|
|
#endif
|
|
default:
|
|
return ECC_CURVE_INVALID;
|
|
}
|
|
}
|
|
|
|
static INLINE const char *PrimeNameForId(byte id)
|
|
{
|
|
switch (id) {
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
|
case ID_ECDSA_SHA2_NISTP256:
|
|
return "nistp256";
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
|
|
case ID_ECDSA_SHA2_NISTP384:
|
|
return "nistp384";
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
|
|
case ID_ECDSA_SHA2_NISTP521:
|
|
return "nistp521";
|
|
#endif
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
#endif /* WOLFSSH_NO_ECDSA */
|
|
|
|
|
|
static INLINE byte AeadModeForId(byte id)
|
|
{
|
|
switch (id) {
|
|
#ifndef WOLFSSH_NO_AES_GCM
|
|
case ID_AES128_GCM:
|
|
case ID_AES192_GCM:
|
|
case ID_AES256_GCM:
|
|
return 1;
|
|
#endif
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/* We have pairs of PubKey types that use the same signature,
|
|
* i.e. ecdsa-sha2-nistp256 and x509v3-ecdsa-sha2-nistp256. */
|
|
static INLINE byte SigTypeForId(byte id)
|
|
{
|
|
switch (id) {
|
|
#ifndef WOLFSSH_NO_SSHA_RSA_SHA1
|
|
#ifdef WOLFSSH_CERTS
|
|
case ID_X509V3_SSH_RSA:
|
|
#endif
|
|
case ID_SSH_RSA:
|
|
return ID_SSH_RSA;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
|
#ifdef WOLFSSH_CERTS
|
|
case ID_X509V3_ECDSA_SHA2_NISTP256:
|
|
#endif
|
|
case ID_ECDSA_SHA2_NISTP256:
|
|
return ID_ECDSA_SHA2_NISTP256;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
|
|
#ifdef WOLFSSH_CERTS
|
|
case ID_X509V3_ECDSA_SHA2_NISTP384:
|
|
#endif
|
|
case ID_ECDSA_SHA2_NISTP384:
|
|
return ID_ECDSA_SHA2_NISTP384;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
|
|
#ifdef WOLFSSH_CERTS
|
|
case ID_X509V3_ECDSA_SHA2_NISTP521:
|
|
#endif
|
|
case ID_ECDSA_SHA2_NISTP521:
|
|
return ID_ECDSA_SHA2_NISTP521;
|
|
#endif
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
int side = WOLFSSH_ENDPOINT_SERVER;
|
|
byte algoId;
|
|
byte list[16] = {ID_NONE};
|
|
word32 listSz;
|
|
word32 skipSz;
|
|
word32 begin;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoKexInit()");
|
|
|
|
if (ssh == NULL || ssh->ctx == NULL ||
|
|
buf == NULL || len == 0 || idx == NULL) {
|
|
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
/*
|
|
* I don't need to save what the client sends here. I should decode
|
|
* each list into a local array of IDs, and pick the one the peer is
|
|
* using that's on my known list, or verify that the one the peer can
|
|
* support the other direction is on my known list. All I need to do
|
|
* is save the actual values.
|
|
*/
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (ssh->handshake == NULL) {
|
|
ssh->handshake = HandshakeInfoNew(ssh->ctx->heap);
|
|
if (ssh->handshake == NULL) {
|
|
WLOG(WS_LOG_DEBUG, "Couldn't allocate handshake info");
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
side = ssh->ctx->side;
|
|
|
|
/* Check that the cookie exists inside the message */
|
|
if (begin + COOKIE_SZ > len) {
|
|
/* error, out of bounds */
|
|
ret = WS_PARSE_E;
|
|
}
|
|
else {
|
|
/* Move past the cookie. */
|
|
begin += COOKIE_SZ;
|
|
}
|
|
}
|
|
|
|
/* KEX Algorithms */
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "DKI: KEX Algorithms");
|
|
listSz = sizeof(list);
|
|
ret = GetNameList(list, &listSz, buf, len, &begin);
|
|
if (ret == WS_SUCCESS) {
|
|
ssh->handshake->kexIdGuess = list[0];
|
|
algoId = MatchIdLists(side, list, listSz,
|
|
cannedKexAlgo, cannedKexAlgoSz);
|
|
if (algoId == ID_UNKNOWN) {
|
|
WLOG(WS_LOG_DEBUG, "Unable to negotiate KEX Algo");
|
|
ret = WS_MATCH_KEX_ALGO_E;
|
|
}
|
|
else {
|
|
ssh->handshake->kexId = algoId;
|
|
ssh->handshake->hashId = HashForId(algoId);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Server Host Key Algorithms */
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "DKI: Server Host Key Algorithms");
|
|
listSz = sizeof(list);
|
|
ret = GetNameList(list, &listSz, buf, len, &begin);
|
|
if (ret == WS_SUCCESS) {
|
|
const byte *cannedKeyAlgo = NULL;
|
|
word32 cannedKeyAlgoSz = 0;
|
|
|
|
if (side == WOLFSSH_ENDPOINT_SERVER) {
|
|
if (ssh->ctx->useEcc) {
|
|
switch (ssh->ctx->useEcc) {
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
|
case ECC_SECP256R1:
|
|
if (ssh->ctx->useCert) {
|
|
#ifdef WOLFSSH_CERTS
|
|
cannedKeyAlgo = cannedKeyAlgoX509Ecc256;
|
|
cannedKeyAlgoSz = cannedKeyAlgoX509Ecc256Sz;
|
|
#endif
|
|
}
|
|
else {
|
|
cannedKeyAlgo = cannedKeyAlgoEcc256;
|
|
cannedKeyAlgoSz = cannedKeyAlgoEcc256Sz;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
|
|
case ECC_SECP384R1:
|
|
if (ssh->ctx->useCert) {
|
|
#ifdef WOLFSSH_CERTS
|
|
cannedKeyAlgo = cannedKeyAlgoX509Ecc384;
|
|
cannedKeyAlgoSz = cannedKeyAlgoX509Ecc384Sz;
|
|
#endif
|
|
}
|
|
else {
|
|
cannedKeyAlgo = cannedKeyAlgoEcc384;
|
|
cannedKeyAlgoSz = cannedKeyAlgoEcc384Sz;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
|
|
case ECC_SECP521R1:
|
|
if (ssh->ctx->useCert) {
|
|
#ifdef WOLFSSH_CERTS
|
|
cannedKeyAlgo = cannedKeyAlgoX509Ecc521;
|
|
cannedKeyAlgoSz = cannedKeyAlgoX509Ecc521Sz;
|
|
#endif
|
|
}
|
|
else {
|
|
cannedKeyAlgo = cannedKeyAlgoEcc521;
|
|
cannedKeyAlgoSz = cannedKeyAlgoEcc521Sz;
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
#ifndef WOLFSSH_NO_SSH_RSA_SHA1
|
|
if (ssh->ctx->useCert) {
|
|
#ifdef WOLFSSH_CERTS
|
|
cannedKeyAlgo = cannedKeyAlgoX509Rsa;
|
|
cannedKeyAlgoSz = cannedKeyAlgoX509RsaSz;
|
|
#endif
|
|
}
|
|
else {
|
|
cannedKeyAlgo = cannedKeyAlgoRsa;
|
|
cannedKeyAlgoSz = cannedKeyAlgoRsaSz;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
/* XXX Does this need to be different for client? */
|
|
cannedKeyAlgo = cannedKeyAlgoClient;
|
|
cannedKeyAlgoSz = cannedKeyAlgoClientSz;
|
|
}
|
|
algoId = MatchIdLists(side, list, listSz,
|
|
cannedKeyAlgo, cannedKeyAlgoSz);
|
|
if (algoId == ID_UNKNOWN) {
|
|
WLOG(WS_LOG_DEBUG, "Unable to negotiate Server Host Key Algo");
|
|
return WS_MATCH_KEY_ALGO_E;
|
|
}
|
|
else {
|
|
ssh->handshake->pubKeyId = algoId;
|
|
ssh->handshake->sigId = SigTypeForId(algoId);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Enc Algorithms - Client to Server */
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "DKI: Enc Algorithms - Client to Server");
|
|
listSz = sizeof(list);
|
|
ret = GetNameList(list, &listSz, buf, len, &begin);
|
|
if (ret == WS_SUCCESS) {
|
|
algoId = MatchIdLists(side, list, listSz,
|
|
cannedEncAlgo, cannedEncAlgoSz);
|
|
if (algoId == ID_UNKNOWN) {
|
|
WLOG(WS_LOG_DEBUG, "Unable to negotiate Encryption Algo C2S");
|
|
ret = WS_MATCH_ENC_ALGO_E;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Enc Algorithms - Server to Client */
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "DKI: Enc Algorithms - Server to Client");
|
|
listSz = sizeof(list);
|
|
ret = GetNameList(list, &listSz, buf, len, &begin);
|
|
if (MatchIdLists(side, list, listSz, &algoId, 1) == ID_UNKNOWN) {
|
|
WLOG(WS_LOG_DEBUG, "Unable to negotiate Encryption Algo S2C");
|
|
ret = WS_MATCH_ENC_ALGO_E;
|
|
}
|
|
else {
|
|
ssh->handshake->encryptId = algoId;
|
|
ssh->handshake->aeadMode = AeadModeForId(algoId);
|
|
ssh->handshake->blockSz = BlockSzForId(algoId);
|
|
ssh->handshake->keys.encKeySz =
|
|
ssh->handshake->peerKeys.encKeySz =
|
|
KeySzForId(algoId);
|
|
if (!ssh->handshake->aeadMode) {
|
|
ssh->handshake->keys.ivSz =
|
|
ssh->handshake->peerKeys.ivSz =
|
|
ssh->handshake->blockSz;
|
|
}
|
|
else {
|
|
#ifndef WOLFSSH_NO_AEAD
|
|
ssh->handshake->keys.ivSz =
|
|
ssh->handshake->peerKeys.ivSz =
|
|
AEAD_NONCE_SZ;
|
|
ssh->handshake->macSz = ssh->handshake->blockSz;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/* MAC Algorithms - Client to Server */
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "DKI: MAC Algorithms - Client to Server");
|
|
listSz = sizeof(list);
|
|
ret = GetNameList(list, &listSz, buf, len, &begin);
|
|
if (ret == WS_SUCCESS && !ssh->aeadMode) {
|
|
algoId = MatchIdLists(side, list, listSz,
|
|
cannedMacAlgo, cannedMacAlgoSz);
|
|
if (algoId == ID_UNKNOWN) {
|
|
WLOG(WS_LOG_DEBUG, "Unable to negotiate MAC Algo C2S");
|
|
ret = WS_MATCH_ENC_ALGO_E;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* MAC Algorithms - Server to Client */
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "DKI: MAC Algorithms - Server to Client");
|
|
listSz = sizeof(list);
|
|
ret = GetNameList(list, &listSz, buf, len, &begin);
|
|
if (ret == WS_SUCCESS && !ssh->handshake->aeadMode) {
|
|
if (MatchIdLists(side, list, listSz, &algoId, 1) == ID_UNKNOWN) {
|
|
WLOG(WS_LOG_DEBUG, "Unable to negotiate MAC Algo S2C");
|
|
ret = WS_MATCH_MAC_ALGO_E;
|
|
}
|
|
else {
|
|
ssh->handshake->macId = algoId;
|
|
ssh->handshake->macSz = MacSzForId(algoId);
|
|
ssh->handshake->keys.macKeySz =
|
|
ssh->handshake->peerKeys.macKeySz =
|
|
KeySzForId(algoId);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Compression Algorithms - Client to Server */
|
|
if (ret == WS_SUCCESS) {
|
|
/* The compression algorithm lists should have none as a value. */
|
|
algoId = ID_NONE;
|
|
|
|
WLOG(WS_LOG_DEBUG, "DKI: Compression Algorithms - Client to Server");
|
|
listSz = sizeof(list);
|
|
ret = GetNameList(list, &listSz, buf, len, &begin);
|
|
if (ret == WS_SUCCESS) {
|
|
if (MatchIdLists(side, list, listSz, &algoId, 1) == ID_UNKNOWN) {
|
|
WLOG(WS_LOG_DEBUG, "Unable to negotiate Compression Algo C2S");
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Compression Algorithms - Server to Client */
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "DKI: Compression Algorithms - Server to Client");
|
|
listSz = sizeof(list);
|
|
ret = GetNameList(list, &listSz, buf, len, &begin);
|
|
if (ret == WS_SUCCESS) {
|
|
if (MatchIdLists(side, list, listSz, &algoId, 1) == ID_UNKNOWN) {
|
|
WLOG(WS_LOG_DEBUG, "Unable to negotiate Compression Algo S2C");
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Languages - Client to Server, skip */
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "DKI: Languages - Client to Server");
|
|
ret = GetUint32(&skipSz, buf, len, &begin);
|
|
if (ret == WS_SUCCESS)
|
|
begin += skipSz;
|
|
}
|
|
|
|
/* Languages - Server to Client, skip */
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "DKI: Languages - Server to Client");
|
|
ret = GetUint32(&skipSz, buf, len, &begin);
|
|
if (ret == WS_SUCCESS)
|
|
begin += skipSz;
|
|
}
|
|
|
|
/* First KEX Packet Follows */
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "DKI: KEX Packet Follows");
|
|
ret = GetBoolean(&ssh->handshake->kexPacketFollows, buf, len, &begin);
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, " packet follows: %s",
|
|
ssh->handshake->kexPacketFollows ? "yes" : "no");
|
|
}
|
|
}
|
|
|
|
/* Skip the "for future use" length. */
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "DKI: For Future Use");
|
|
ret = GetUint32(&skipSz, buf, len, &begin);
|
|
if (ret == WS_SUCCESS)
|
|
begin += skipSz;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
enum wc_HashType enmhashId = (enum wc_HashType)ssh->handshake->hashId;
|
|
byte scratchLen[LENGTH_SZ];
|
|
word32 strSz;
|
|
|
|
if (!ssh->isKeying) {
|
|
WLOG(WS_LOG_DEBUG, "Keying initiated");
|
|
ret = SendKexInit(ssh);
|
|
}
|
|
|
|
/* account for possible want write case from SendKexInit */
|
|
if (ret == WS_SUCCESS || ret == WS_WANT_WRITE)
|
|
ret = wc_HashInit(&ssh->handshake->hash, enmhashId);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
ssh->peerProtoId, ssh->peerProtoIdSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
byte SSH_PROTO_EOL_SZ = 2;
|
|
|
|
strSz = (word32)WSTRLEN(sshProtoIdStr) - SSH_PROTO_EOL_SZ;
|
|
c32toa(strSz, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash, enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash, enmhashId,
|
|
(const byte*)sshProtoIdStr, strSz);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (ssh->ctx->side == WOLFSSH_ENDPOINT_CLIENT) {
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
ssh->peerProtoId, ssh->peerProtoIdSz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
ssh->handshake->kexInit,
|
|
ssh->handshake->kexInitSz);
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
c32toa(len + 1, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash, enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
scratchLen[0] = MSGID_KEXINIT;
|
|
ret = wc_HashUpdate(&ssh->handshake->hash, enmhashId,
|
|
scratchLen, MSG_ID_SZ);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash, enmhashId,
|
|
buf, len);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
ssh->handshake->kexInit,
|
|
ssh->handshake->kexInitSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
*idx = begin;
|
|
if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER)
|
|
ssh->clientState = CLIENT_KEXINIT_DONE;
|
|
else
|
|
ssh->serverState = SERVER_KEXINIT_DONE;
|
|
|
|
if (ssh->error != 0)
|
|
ret = ssh->error; /* propogate potential want write case from
|
|
SendKexInit*/
|
|
}
|
|
}
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoKexInit(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* create mpint type
|
|
*
|
|
* can decrease size of buf by 1 or more if leading bytes are 0's and not needed
|
|
* the input argument "sz" gets reset if that is the case. Buffer size is never
|
|
* increased.
|
|
*
|
|
* An example of this would be a buffer of 0053 changed to 53.
|
|
* If a padding value is needed then "pad" is set to 1
|
|
*
|
|
*/
|
|
static int CreateMpint(byte* buf, word32* sz, byte* pad)
|
|
{
|
|
word32 i;
|
|
|
|
if (buf == NULL || sz == NULL || pad == NULL) {
|
|
WLOG(WS_LOG_ERROR, "Internal argument error with CreateMpint");
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
if (*sz == 0)
|
|
return WS_SUCCESS;
|
|
|
|
/* check for leading 0's */
|
|
for (i = 0; i < *sz; i++) {
|
|
if (buf[i] != 0x00)
|
|
break;
|
|
}
|
|
*pad = (buf[i] & 0x80) ? 1 : 0;
|
|
|
|
/* if padding would be needed and have leading 0's already then do not add
|
|
* extra 0's */
|
|
if (i > 0 && *pad == 1) {
|
|
i = i - 1;
|
|
*pad = 0;
|
|
}
|
|
|
|
/* if i is still greater than 0 then the buffer needs shifted to remove
|
|
* leading 0's */
|
|
if (i > 0) {
|
|
WMEMMOVE(buf, buf + i, *sz - i);
|
|
*sz = *sz - i;
|
|
}
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
#if !defined(WOLFSSH_NO_DH_GROUP1_SHA1) || \
|
|
!defined(WOLFSSH_NO_DH_GROUP14_SHA1) || \
|
|
!defined(WOLFSSH_NO_DH_GEX_SHA256)
|
|
static const byte dhGenerator[] = { 2 };
|
|
static const word32 dhGeneratorSz = sizeof(dhGenerator);
|
|
#endif
|
|
|
|
#ifndef WOLFSSH_NO_DH_GROUP1_SHA1
|
|
static const byte dhPrimeGroup1[] = {
|
|
/* SSH DH Group 1 (Oakley Group 2, 1024-bit MODP Group, RFC 2409) */
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
|
|
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
|
|
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
|
|
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
|
|
0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
|
|
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
|
|
0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
|
|
0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
|
|
0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
|
|
0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
|
|
0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
|
|
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
|
|
0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
|
|
0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
|
};
|
|
static const word32 dhPrimeGroup1Sz = sizeof(dhPrimeGroup1);
|
|
#endif
|
|
|
|
#if !defined(WOLFSSH_NO_DH_GROUP14_SHA1) || \
|
|
!defined(WOLFSSH_NO_DH_GEX_SHA256)
|
|
static const byte dhPrimeGroup14[] = {
|
|
/* SSH DH Group 14 (Oakley Group 14, 2048-bit MODP Group, RFC 3526) */
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
|
|
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
|
|
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
|
|
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
|
|
0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
|
|
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
|
|
0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
|
|
0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
|
|
0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
|
|
0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
|
|
0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
|
|
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
|
|
0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
|
|
0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
|
|
0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
|
|
0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
|
|
0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
|
|
0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
|
|
0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
|
|
0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
|
|
0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
|
|
0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
|
|
0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
|
|
0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
|
|
0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
|
|
0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
|
|
0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
|
|
0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
|
|
0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
|
|
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
|
};
|
|
static const word32 dhPrimeGroup14Sz = sizeof(dhPrimeGroup14);
|
|
#endif
|
|
|
|
|
|
static int DoKexDhInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
|
|
{
|
|
/* First get the length of the MP_INT, and then add in the hash of the
|
|
* mp_int value of e as it appears in the packet. After that, decode e
|
|
* into an mp_int struct for the DH calculation by wolfCrypt.
|
|
*
|
|
* This function also works as MSGID_KEXECDH_INIT (30). That message
|
|
* has the same format as MSGID_KEXDH_INIT, except it is the ECDH Q value
|
|
* in the message isn't of the DH e value. Treat the Q as e. */
|
|
/* DYNTYPE_DH */
|
|
|
|
byte* e;
|
|
word32 eSz;
|
|
word32 begin;
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL || ssh->handshake == NULL || buf == NULL || len == 0 ||
|
|
idx == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (ssh->handshake->kexPacketFollows
|
|
&& ssh->handshake->kexIdGuess != ssh->handshake->kexId) {
|
|
|
|
/* skip this message. */
|
|
WLOG(WS_LOG_DEBUG, "Skipping the client's KEX init function.");
|
|
ssh->handshake->kexPacketFollows = 0;
|
|
*idx += len;
|
|
return WS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
ret = GetUint32(&eSz, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
/* Validate eSz */
|
|
if ((len < begin) || (eSz > len - begin)) {
|
|
ret = WS_RECV_OVERFLOW_E;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
e = buf + begin;
|
|
begin += eSz;
|
|
|
|
if (eSz <= sizeof(ssh->handshake->e)) {
|
|
WMEMCPY(ssh->handshake->e, e, eSz);
|
|
ssh->handshake->eSz = eSz;
|
|
}
|
|
|
|
ssh->clientState = CLIENT_KEXDH_INIT_DONE;
|
|
*idx = begin;
|
|
|
|
ret = SendKexDhReply(ssh);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
struct wolfSSH_sigKeyBlock {
|
|
byte useRsa;
|
|
word32 keySz;
|
|
union {
|
|
#ifndef WOLFSSH_NO_RSA
|
|
struct {
|
|
RsaKey key;
|
|
} rsa;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
struct {
|
|
ecc_key key;
|
|
} ecc;
|
|
#endif
|
|
} sk;
|
|
};
|
|
|
|
static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
|
|
{
|
|
enum wc_HashType enmhashId;
|
|
byte* pubKey = NULL;
|
|
word32 pubKeySz;
|
|
byte* f = NULL;
|
|
word32 fSz;
|
|
byte* sig;
|
|
word32 sigSz;
|
|
word32 scratch;
|
|
byte scratchLen[LENGTH_SZ];
|
|
byte kPad = 0;
|
|
word32 begin;
|
|
int ret = WS_SUCCESS;
|
|
int tmpIdx = 0;
|
|
struct wolfSSH_sigKeyBlock *sigKeyBlock_ptr = NULL;
|
|
#ifndef WOLFSSH_NO_ECDH
|
|
ecc_key *key_ptr = NULL;
|
|
#ifndef WOLFSSH_SMALL_STACK
|
|
ecc_key key_s;
|
|
#endif
|
|
#endif
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoKexDhReply()");
|
|
|
|
if (ssh == NULL || ssh->handshake == NULL || buf == NULL ||
|
|
len == 0 || idx == NULL) {
|
|
ret = WS_BAD_ARGUMENT;
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoKexDhReply(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS && len < LENGTH_SZ*2 + *idx) {
|
|
ret = WS_BUFFER_E;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
ret = GetUint32(&pubKeySz, buf, len, &begin);
|
|
if (ret == WS_SUCCESS && (pubKeySz > len - LENGTH_SZ - begin )) {
|
|
ret = WS_BUFFER_E;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
pubKey = buf + begin;
|
|
if (ssh->ctx->publicKeyCheckCb != NULL) {
|
|
WLOG(WS_LOG_DEBUG, "DKDR: Calling the public key check callback");
|
|
ret = ssh->ctx->publicKeyCheckCb(pubKey, pubKeySz,
|
|
ssh->publicKeyCheckCtx);
|
|
if (ret == 0) {
|
|
WLOG(WS_LOG_DEBUG, "DKDR: public key accepted");
|
|
ret = WS_SUCCESS;
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "DKDR: public key rejected");
|
|
ret = WS_PUBKEY_REJECTED_E;
|
|
}
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "DKDR: no public key check callback, accepted");
|
|
ret = WS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
enmhashId = (enum wc_HashType)ssh->handshake->hashId;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
/* Hash in the raw public key blob from the server including its
|
|
* length which is at LENGTH_SZ offset ahead of pubKey. */
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
pubKey - LENGTH_SZ, pubKeySz + LENGTH_SZ);
|
|
|
|
if (ret == WS_SUCCESS)
|
|
begin += pubKeySz;
|
|
|
|
#ifndef WOLFSSH_NO_DH_GEX_SHA256
|
|
/* If using DH-GEX include the GEX specific values. */
|
|
if (ret == WS_SUCCESS && ssh->handshake->kexId == ID_DH_GEX_SHA256) {
|
|
byte primeGroupPad = 0, generatorPad = 0;
|
|
|
|
if (ssh->handshake->primeGroup == NULL ||
|
|
ssh->handshake->generator == NULL) {
|
|
WLOG(WS_LOG_DEBUG,
|
|
"DKDR: trying GEX without generator or prime group");
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
/* Hash in the client's requested minimum key size. */
|
|
if (ret == 0) {
|
|
c32toa(ssh->handshake->dhGexMinSz, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the client's requested preferred key size. */
|
|
if (ret == 0) {
|
|
c32toa(ssh->handshake->dhGexPreferredSz, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the client's requested maximum key size. */
|
|
if (ret == 0) {
|
|
c32toa(ssh->handshake->dhGexMaxSz, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Add a pad byte if the mpint has the MSB set. */
|
|
if (ret == 0) {
|
|
if (ssh->handshake->primeGroup != NULL &&
|
|
ssh->handshake->primeGroup[0] & 0x80)
|
|
primeGroupPad = 1;
|
|
|
|
/* Hash in the length of the GEX prime group. */
|
|
c32toa(ssh->handshake->primeGroupSz + primeGroupPad,
|
|
scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the pad byte for the GEX prime group. */
|
|
if (ret == 0) {
|
|
if (primeGroupPad) {
|
|
scratchLen[0] = 0;
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, 1);
|
|
}
|
|
}
|
|
/* Hash in the GEX prime group. */
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
ssh->handshake->primeGroup,
|
|
ssh->handshake->primeGroupSz);
|
|
/* Add a pad byte if the mpint has the MSB set. */
|
|
if (ret == 0) {
|
|
if (ssh->handshake->generator[0] & 0x80)
|
|
generatorPad = 1;
|
|
|
|
/* Hash in the length of the GEX generator. */
|
|
c32toa(ssh->handshake->generatorSz + generatorPad, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the pad byte for the GEX generator. */
|
|
if (ret == 0) {
|
|
if (generatorPad) {
|
|
scratchLen[0] = 0;
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, 1);
|
|
}
|
|
}
|
|
/* Hash in the GEX generator. */
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
ssh->handshake->generator,
|
|
ssh->handshake->generatorSz);
|
|
}
|
|
#endif
|
|
|
|
/* Hash in the size of the client's DH e-value (ECDH Q-value). */
|
|
if (ret == 0) {
|
|
c32toa(ssh->handshake->eSz, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash, enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the client's DH e-value (ECDH Q-value). */
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash, enmhashId,
|
|
ssh->handshake->e, ssh->handshake->eSz);
|
|
|
|
/* Get and hash in the server's DH f-value (ECDH Q-value) */
|
|
if (ret == WS_SUCCESS) {
|
|
f = buf + begin;
|
|
ret = GetUint32(&fSz, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (fSz > len - begin) {
|
|
WLOG(WS_LOG_DEBUG, "F size would result in error");
|
|
ret = WS_PARSE_E;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
f, fSz + LENGTH_SZ);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
f = buf + begin;
|
|
begin += fSz;
|
|
ret = GetUint32(&sigSz, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (sigSz > len - begin) {
|
|
WLOG(WS_LOG_DEBUG, "Signature size would result in error 1");
|
|
ret = WS_PARSE_E;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
sigKeyBlock_ptr = (struct wolfSSH_sigKeyBlock*)WMALLOC(
|
|
sizeof(struct wolfSSH_sigKeyBlock), ssh->ctx->heap,
|
|
DYNTYPE_PRIVKEY);
|
|
if (sigKeyBlock_ptr == NULL) {
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
key_ptr = (ecc_key*)WMALLOC(sizeof(ecc_key), ssh->ctx->heap,
|
|
DYNTYPE_PRIVKEY);
|
|
if (key_ptr == NULL) {
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
#endif /* WOLFSSH_NO_ECDSA */
|
|
|
|
#else /* ! WOLFSSH_SMALL_STACK */
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
key_ptr = &key_s;
|
|
#endif
|
|
#endif
|
|
}
|
|
if (ret == WS_SUCCESS) {
|
|
sig = buf + begin;
|
|
tmpIdx = begin;
|
|
begin += sigSz;
|
|
*idx = begin;
|
|
|
|
/* Load in the server's public signing key */
|
|
sigKeyBlock_ptr->useRsa = ssh->handshake->pubKeyId == ID_SSH_RSA;
|
|
|
|
if (sigKeyBlock_ptr->useRsa) {
|
|
#ifndef WOLFSSH_NO_RSA
|
|
byte* e = NULL;
|
|
word32 eSz;
|
|
byte* n;
|
|
word32 nSz;
|
|
word32 pubKeyIdx = 0;
|
|
|
|
ret = wc_InitRsaKey(&sigKeyBlock_ptr->sk.rsa.key, ssh->ctx->heap);
|
|
if (ret != 0)
|
|
ret = WS_RSA_E;
|
|
if (ret == 0)
|
|
ret = GetUint32(&scratch, pubKey, pubKeySz, &pubKeyIdx);
|
|
/* This is the algo name. */
|
|
if (ret == WS_SUCCESS) {
|
|
pubKeyIdx += scratch;
|
|
ret = GetUint32(&eSz, pubKey, pubKeySz, &pubKeyIdx);
|
|
if (ret == WS_SUCCESS && eSz > pubKeySz - pubKeyIdx)
|
|
ret = WS_BUFFER_E;
|
|
}
|
|
if (ret == WS_SUCCESS) {
|
|
e = pubKey + pubKeyIdx;
|
|
pubKeyIdx += eSz;
|
|
ret = GetUint32(&nSz, pubKey, pubKeySz, &pubKeyIdx);
|
|
if (ret == WS_SUCCESS && nSz > pubKeySz - pubKeyIdx)
|
|
ret = WS_BUFFER_E;
|
|
}
|
|
if (ret == WS_SUCCESS) {
|
|
n = pubKey + pubKeyIdx;
|
|
ret = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz,
|
|
&sigKeyBlock_ptr->sk.rsa.key);
|
|
}
|
|
|
|
if (ret == 0)
|
|
sigKeyBlock_ptr->keySz = sizeof(sigKeyBlock_ptr->sk.rsa.key);
|
|
else
|
|
ret = WS_RSA_E;
|
|
#else
|
|
(void)tmpIdx;
|
|
ret = WS_INVALID_ALGO_ID;
|
|
#endif
|
|
} else {
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
byte* q;
|
|
word32 qSz, pubKeyIdx = 0;
|
|
int primeId;
|
|
|
|
ret = wc_ecc_init_ex(&sigKeyBlock_ptr->sk.ecc.key, ssh->ctx->heap,
|
|
INVALID_DEVID);
|
|
#ifdef HAVE_WC_ECC_SET_RNG
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_ecc_set_rng(&sigKeyBlock_ptr->sk.ecc.key, ssh->rng);
|
|
#endif
|
|
if (ret != 0)
|
|
ret = WS_ECC_E;
|
|
else
|
|
ret = GetStringRef(&qSz, &q, pubKey, pubKeySz, &pubKeyIdx);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
primeId = (int)NameToId((const char*)q, qSz);
|
|
if (primeId != ID_UNKNOWN) {
|
|
primeId = wcPrimeForId((byte)primeId);
|
|
if (primeId == ECC_CURVE_INVALID)
|
|
ret = WS_INVALID_PRIME_CURVE;
|
|
}
|
|
else
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
|
|
/* Skip the curve name since we're getting it from the algo. */
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&scratch, pubKey, pubKeySz, &pubKeyIdx);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
pubKeyIdx += scratch;
|
|
ret = GetStringRef(&qSz, &q, pubKey, pubKeySz, &pubKeyIdx);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = wc_ecc_import_x963_ex(q, qSz,
|
|
&sigKeyBlock_ptr->sk.ecc.key, primeId);
|
|
if (ret == 0)
|
|
sigKeyBlock_ptr->keySz = sizeof(sigKeyBlock_ptr->sk.ecc.key);
|
|
else
|
|
ret = WS_ECC_E;
|
|
}
|
|
#else
|
|
ret = WS_INVALID_ALGO_ID;
|
|
#endif
|
|
}
|
|
|
|
/* Generate and hash in the shared secret */
|
|
if (ret == 0) {
|
|
/* reset size here because a previous shared secret could
|
|
* potentially be smaller by a byte than usual and cause buffer
|
|
* issues with re-key */
|
|
ssh->kSz = MAX_KEX_KEY_SZ;
|
|
if (!ssh->handshake->useEcc
|
|
#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256
|
|
&& !ssh->handshake->useSaber
|
|
#endif
|
|
) {
|
|
#ifndef WOLFSSH_NO_DH
|
|
#ifdef PRIVATE_KEY_UNLOCK
|
|
PRIVATE_KEY_UNLOCK();
|
|
#endif
|
|
ret = wc_DhAgree(&ssh->handshake->privKey.dh,
|
|
ssh->k, &ssh->kSz,
|
|
ssh->handshake->x, ssh->handshake->xSz,
|
|
f, fSz);
|
|
#ifdef PRIVATE_KEY_LOCK
|
|
PRIVATE_KEY_LOCK();
|
|
#endif
|
|
ForceZero(ssh->handshake->x, ssh->handshake->xSz);
|
|
wc_FreeDhKey(&ssh->handshake->privKey.dh);
|
|
if (ret != 0) {
|
|
WLOG(WS_LOG_ERROR,
|
|
"Generate DH shared secret failed, %d", ret);
|
|
}
|
|
#else
|
|
ret = WS_INVALID_ALGO_ID;
|
|
#endif
|
|
}
|
|
else if (ssh->handshake->useEcc) {
|
|
#ifndef WOLFSSH_NO_ECDH
|
|
ret = wc_ecc_init(key_ptr);
|
|
#ifdef HAVE_WC_ECC_SET_RNG
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_ecc_set_rng(key_ptr, ssh->rng);
|
|
#endif
|
|
if (ret == 0)
|
|
ret = wc_ecc_import_x963(f, fSz, key_ptr);
|
|
if (ret == 0) {
|
|
#ifdef PRIVATE_KEY_UNLOCK
|
|
PRIVATE_KEY_UNLOCK();
|
|
#endif
|
|
ret = wc_ecc_shared_secret(&ssh->handshake->privKey.ecc,
|
|
key_ptr, ssh->k, &ssh->kSz);
|
|
#ifdef PRIVATE_KEY_LOCK
|
|
PRIVATE_KEY_LOCK();
|
|
#endif
|
|
}
|
|
wc_ecc_free(key_ptr);
|
|
wc_ecc_free(&ssh->handshake->privKey.ecc);
|
|
if (ret != 0) {
|
|
WLOG(WS_LOG_ERROR,
|
|
"Generate ECC shared secret failed, %d", ret);
|
|
}
|
|
#else
|
|
ret = WS_INVALID_ALGO_ID;
|
|
#endif
|
|
}
|
|
#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256
|
|
else if (ssh->handshake->useSaber) {
|
|
/* This is a KEM. In this case, I need to decapsulate the
|
|
* ciphertext. */
|
|
OQS_KEM* kem = NULL;
|
|
ret = 0;
|
|
|
|
if (ret == 0) {
|
|
kem = OQS_KEM_new(OQS_KEM_alg_saber_lightsaber);
|
|
if (kem == NULL) {
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
}
|
|
|
|
if (ret == 0) {
|
|
if (fSz != kem->length_ciphertext) {
|
|
ret = WS_BUFFER_E;
|
|
}
|
|
}
|
|
|
|
if (OQS_KEM_decaps(kem, ssh->k, f, ssh->handshake->x)
|
|
!= OQS_SUCCESS) {
|
|
ret = WS_ERROR;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
ssh->kSz = kem->length_shared_secret;
|
|
}
|
|
}
|
|
#endif
|
|
else {
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
}
|
|
if (ret == 0)
|
|
ret = CreateMpint(ssh->k, &ssh->kSz, &kPad);
|
|
|
|
/* Hash in the shared secret K. */
|
|
if (ret == 0) {
|
|
c32toa(ssh->kSz + kPad, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash, enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
if (ret == 0) {
|
|
if (kPad) {
|
|
scratchLen[0] = 0;
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId, scratchLen, 1);
|
|
}
|
|
}
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash, enmhashId,
|
|
ssh->k, ssh->kSz);
|
|
|
|
/* Save the exchange hash value H, and session ID. */
|
|
if (ret == 0) {
|
|
ret = wc_HashFinal(&ssh->handshake->hash,
|
|
enmhashId, ssh->h);
|
|
wc_HashFree(&ssh->handshake->hash, enmhashId);
|
|
ssh->handshake->hashId = WC_HASH_TYPE_NONE;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
ssh->hSz = wc_HashGetDigestSize(enmhashId);
|
|
if (ssh->sessionIdSz == 0) {
|
|
WMEMCPY(ssh->sessionId, ssh->h, ssh->hSz);
|
|
ssh->sessionIdSz = ssh->hSz;
|
|
}
|
|
}
|
|
|
|
if (ret != WS_SUCCESS)
|
|
ret = WS_CRYPTO_FAILED;
|
|
|
|
/* Verify h with the server's public key. */
|
|
if (ret == WS_SUCCESS) {
|
|
/* Skip past the sig name. Check it, though. Other SSH
|
|
* implementations do the verify based on the name, despite what
|
|
* was agreed upon. XXX*/
|
|
begin = 0;
|
|
ret = GetUint32(&scratch, sig, sigSz, &begin);
|
|
if (ret == WS_SUCCESS) {
|
|
/* Check that scratch isn't larger than the remainder of the
|
|
* sig buffer and leaves enough room for another length. */
|
|
if (scratch > sigSz - begin - LENGTH_SZ) {
|
|
WLOG(WS_LOG_DEBUG, "sig name size is too large");
|
|
ret = WS_PARSE_E;
|
|
}
|
|
}
|
|
if (ret == WS_SUCCESS) {
|
|
begin += scratch;
|
|
ret = GetUint32(&scratch, sig, sigSz, &begin);
|
|
}
|
|
if (ret == WS_SUCCESS) {
|
|
if (scratch > sigSz - begin) {
|
|
WLOG(WS_LOG_DEBUG, "sig name size is too large");
|
|
ret = WS_PARSE_E;
|
|
}
|
|
}
|
|
if (ret == WS_SUCCESS) {
|
|
if (sigKeyBlock_ptr->useRsa) {
|
|
#ifndef WOLFSSH_NO_RSA
|
|
sig = sig + begin;
|
|
/* In the fuzz, sigSz ends up 1 and it has issues. */
|
|
sigSz = scratch;
|
|
|
|
if (sigSz < MIN_RSA_SIG_SZ) {
|
|
WLOG(WS_LOG_DEBUG, "Provided signature is too small.");
|
|
ret = WS_RSA_E;
|
|
}
|
|
|
|
if (sigSz + begin + tmpIdx > len) {
|
|
WLOG(WS_LOG_DEBUG,
|
|
"Signature size found would result in error 2");
|
|
ret = WS_BUFFER_E;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = wc_SignatureVerify(
|
|
HashForId(ssh->handshake->pubKeyId),
|
|
WC_SIGNATURE_TYPE_RSA_W_ENC,
|
|
ssh->h, ssh->hSz, sig, sigSz,
|
|
&sigKeyBlock_ptr->sk, sigKeyBlock_ptr->keySz);
|
|
if (ret != 0) {
|
|
WLOG(WS_LOG_DEBUG,
|
|
"DoKexDhReply: Signature Verify fail (%d)",
|
|
ret);
|
|
ret = WS_RSA_E;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else {
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
byte* r;
|
|
byte* s;
|
|
word32 rSz, sSz, asnSigSz;
|
|
byte asnSig[256];
|
|
|
|
sig = sig + begin;
|
|
sigSz = scratch;
|
|
begin = 0;
|
|
asnSigSz = sizeof(asnSig);
|
|
XMEMSET(asnSig, 0, asnSigSz);
|
|
|
|
ret = GetStringRef(&rSz, &r, sig, sigSz, &begin);
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetStringRef(&sSz, &s, sig, sigSz, &begin);
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_ecc_rs_raw_to_sig(r, rSz, s, sSz,
|
|
asnSig, &asnSigSz);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = wc_SignatureVerify(
|
|
HashForId(ssh->handshake->pubKeyId),
|
|
WC_SIGNATURE_TYPE_ECC,
|
|
ssh->h, ssh->hSz, asnSig, asnSigSz,
|
|
&sigKeyBlock_ptr->sk, sigKeyBlock_ptr->keySz);
|
|
if (ret != 0) {
|
|
WLOG(WS_LOG_DEBUG,
|
|
"DoKexDhReply: Signature Verify fail (%d)",
|
|
ret);
|
|
ret = WS_ECC_E;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sigKeyBlock_ptr->useRsa) {
|
|
#ifndef WOLFSSH_NO_RSA
|
|
wc_FreeRsaKey(&sigKeyBlock_ptr->sk.rsa.key);
|
|
#endif
|
|
}
|
|
else {
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
wc_ecc_free(&sigKeyBlock_ptr->sk.ecc.key);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GenerateKeys(ssh, enmhashId);
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = SendNewKeys(ssh);
|
|
|
|
if (sigKeyBlock_ptr)
|
|
WFREE(sigKeyBlock_ptr, ssh->ctx->heap, DYNTYPE_PRIVKEY);
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
if (key_ptr)
|
|
WFREE(key_ptr, ssh->ctx->heap, DYNTYPE_PRIVKEY);
|
|
#endif
|
|
#endif
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoKexDhReply(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int DoNewKeys(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
(void)buf;
|
|
(void)len;
|
|
(void)idx;
|
|
|
|
if (ssh == NULL || ssh->handshake == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ssh->peerEncryptId = ssh->handshake->encryptId;
|
|
ssh->peerMacId = ssh->handshake->macId;
|
|
ssh->peerBlockSz = ssh->handshake->blockSz;
|
|
ssh->peerMacSz = ssh->handshake->macSz;
|
|
ssh->peerAeadMode = ssh->handshake->aeadMode;
|
|
WMEMCPY(&ssh->peerKeys, &ssh->handshake->peerKeys, sizeof(Keys));
|
|
|
|
switch (ssh->peerEncryptId) {
|
|
case ID_NONE:
|
|
WLOG(WS_LOG_DEBUG, "DNK: peer using cipher none");
|
|
break;
|
|
|
|
#ifndef WOLFSSH_NO_AES_CBC
|
|
case ID_AES128_CBC:
|
|
case ID_AES192_CBC:
|
|
case ID_AES256_CBC:
|
|
WLOG(WS_LOG_DEBUG, "DNK: peer using cipher aes-cbc");
|
|
ret = wc_AesSetKey(&ssh->decryptCipher.aes,
|
|
ssh->peerKeys.encKey, ssh->peerKeys.encKeySz,
|
|
ssh->peerKeys.iv, AES_DECRYPTION);
|
|
break;
|
|
#endif
|
|
|
|
#ifndef WOLFSSH_NO_AES_CTR
|
|
case ID_AES128_CTR:
|
|
case ID_AES192_CTR:
|
|
case ID_AES256_CTR:
|
|
WLOG(WS_LOG_DEBUG, "DNK: peer using cipher aes-ctr");
|
|
ret = wc_AesSetKey(&ssh->decryptCipher.aes,
|
|
ssh->peerKeys.encKey, ssh->peerKeys.encKeySz,
|
|
ssh->peerKeys.iv, AES_ENCRYPTION);
|
|
break;
|
|
#endif
|
|
|
|
#ifndef WOLFSSH_NO_AES_GCM
|
|
case ID_AES128_GCM:
|
|
case ID_AES192_GCM:
|
|
case ID_AES256_GCM:
|
|
WLOG(WS_LOG_DEBUG, "DNK: peer using cipher aes-gcm");
|
|
ret = wc_AesGcmSetKey(&ssh->decryptCipher.aes,
|
|
ssh->peerKeys.encKey,
|
|
ssh->peerKeys.encKeySz);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
WLOG(WS_LOG_DEBUG, "DNK: peer using cipher invalid");
|
|
break;
|
|
}
|
|
|
|
if (ret == 0)
|
|
ret = WS_SUCCESS;
|
|
else
|
|
ret = WS_CRYPTO_FAILED;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ssh->rxCount = 0;
|
|
ssh->highwaterFlag = 0;
|
|
ssh->isKeying = 0;
|
|
HandshakeInfoFree(ssh->handshake, ssh->ctx->heap);
|
|
ssh->handshake = NULL;
|
|
WLOG(WS_LOG_DEBUG, "Keying completed");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifndef WOLFSSH_NO_DH_GEX_SHA256
|
|
static int DoKexDhGexRequest(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
word32 begin;
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL || ssh->handshake == NULL || buf == NULL || len == 0 ||
|
|
idx == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
ret = GetUint32(&ssh->handshake->dhGexMinSz, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = GetUint32(&ssh->handshake->dhGexPreferredSz, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = GetUint32(&ssh->handshake->dhGexMaxSz, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_INFO, " min = %u, preferred = %u, max = %u",
|
|
ssh->handshake->dhGexMinSz,
|
|
ssh->handshake->dhGexPreferredSz,
|
|
ssh->handshake->dhGexMaxSz);
|
|
*idx = begin;
|
|
ret = SendKexDhGexGroup(ssh);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int DoKexDhGexGroup(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
byte* primeGroup = NULL;
|
|
word32 primeGroupSz;
|
|
byte* generator = NULL;
|
|
word32 generatorSz;
|
|
word32 begin;
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
ret = GetMpint(&primeGroupSz, &primeGroup, buf, len, &begin);
|
|
if (ret == WS_SUCCESS && primeGroupSz > (MAX_KEX_KEY_SZ + 1))
|
|
ret = WS_DH_SIZE_E;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetMpint(&generatorSz, &generator, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (ssh->handshake->primeGroup)
|
|
WFREE(ssh->handshake->primeGroup, ssh->ctx->heap, DYNTYPE_MPINT);
|
|
ssh->handshake->primeGroup =
|
|
(byte*)WMALLOC(primeGroupSz, ssh->ctx->heap, DYNTYPE_MPINT);
|
|
if (ssh->handshake->primeGroup == NULL)
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (ssh->handshake->generator)
|
|
WFREE(ssh->handshake->generator, ssh->ctx->heap, DYNTYPE_MPINT);
|
|
ssh->handshake->generator =
|
|
(byte*)WMALLOC(generatorSz, ssh->ctx->heap, DYNTYPE_MPINT);
|
|
if (ssh->handshake->generator == NULL) {
|
|
ret = WS_MEMORY_E;
|
|
WFREE(ssh->handshake->primeGroup, ssh->ctx->heap, DYNTYPE_MPINT);
|
|
ssh->handshake->primeGroup = NULL;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WMEMCPY(ssh->handshake->primeGroup, primeGroup, primeGroupSz);
|
|
ssh->handshake->primeGroupSz = primeGroupSz;
|
|
WMEMCPY(ssh->handshake->generator, generator, generatorSz);
|
|
ssh->handshake->generatorSz = generatorSz;
|
|
|
|
*idx = begin;
|
|
ret = SendKexDhInit(ssh);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
|
|
static int DoIgnore(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
|
|
{
|
|
word32 dataSz;
|
|
word32 begin = *idx;
|
|
|
|
(void)ssh;
|
|
(void)len;
|
|
|
|
ato32(buf + begin, &dataSz);
|
|
begin += LENGTH_SZ + dataSz;
|
|
|
|
*idx = begin;
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
static int DoRequestSuccess(WOLFSSH *ssh, byte *buf, word32 len, word32 *idx)
|
|
{
|
|
word32 dataSz;
|
|
word32 begin = *idx;
|
|
int ret=WS_SUCCESS;
|
|
|
|
(void)ssh;
|
|
(void)len;
|
|
|
|
WLOG(WS_LOG_DEBUG, "DoRequestSuccess, *idx=%d, len=%d", *idx, len);
|
|
ato32(buf + begin, &dataSz);
|
|
begin += LENGTH_SZ + dataSz;
|
|
|
|
if (ssh->ctx->reqSuccessCb != NULL)
|
|
ret = ssh->ctx->reqSuccessCb(ssh, &(buf[*idx]), len, ssh->reqSuccessCtx);
|
|
|
|
*idx = begin;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int DoRequestFailure(WOLFSSH *ssh, byte *buf, word32 len, word32 *idx)
|
|
{
|
|
word32 dataSz;
|
|
word32 begin = *idx;
|
|
int ret = WS_SUCCESS;
|
|
|
|
(void)ssh;
|
|
(void)len;
|
|
|
|
WLOG(WS_LOG_DEBUG, "DoRequestFalure, *idx=%d, len=%d", *idx, len);
|
|
ato32(buf + begin, &dataSz);
|
|
begin += LENGTH_SZ + dataSz;
|
|
|
|
if (ssh->ctx->reqFailureCb != NULL)
|
|
ret = ssh->ctx->reqFailureCb(ssh, &(buf[*idx]), len, ssh->reqFailureCtx);
|
|
|
|
*idx = begin;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int DoDebug(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
|
|
{
|
|
byte alwaysDisplay;
|
|
char* msg = NULL;
|
|
char* lang = NULL;
|
|
word32 strSz;
|
|
word32 begin;
|
|
|
|
if (ssh == NULL || buf == NULL || idx == NULL ||
|
|
len < (2 * LENGTH_SZ) + 1) {
|
|
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
begin = *idx;
|
|
|
|
alwaysDisplay = buf[begin++];
|
|
|
|
ato32(buf + begin, &strSz);
|
|
begin += LENGTH_SZ;
|
|
if (strSz > 0) {
|
|
if (strSz > len - begin) {
|
|
return WS_BUFFER_E;
|
|
}
|
|
|
|
msg = (char*)WMALLOC(strSz + 1, ssh->ctx->heap, DYNTYPE_STRING);
|
|
if (msg != NULL) {
|
|
WMEMCPY(msg, buf + begin, strSz);
|
|
msg[strSz] = 0;
|
|
}
|
|
else {
|
|
return WS_MEMORY_E;
|
|
}
|
|
begin += strSz;
|
|
}
|
|
|
|
if (LENGTH_SZ > len - begin) {
|
|
WFREE(msg, ssh->ctx->heap, DYNTYPE_STRING);
|
|
return WS_BUFFER_E;
|
|
}
|
|
|
|
ato32(buf + begin, &strSz);
|
|
begin += LENGTH_SZ;
|
|
if (strSz > 0) {
|
|
if ((len < begin) || (strSz > len - begin)) {
|
|
WFREE(msg, ssh->ctx->heap, DYNTYPE_STRING);
|
|
return WS_BUFFER_E;
|
|
}
|
|
|
|
lang = (char*)WMALLOC(strSz + 1, ssh->ctx->heap, DYNTYPE_STRING);
|
|
if (lang != NULL) {
|
|
WMEMCPY(lang, buf + begin, strSz);
|
|
lang[strSz] = 0;
|
|
}
|
|
else {
|
|
WFREE(msg, ssh->ctx->heap, DYNTYPE_STRING);
|
|
return WS_MEMORY_E;
|
|
}
|
|
begin += strSz;
|
|
}
|
|
|
|
if (alwaysDisplay) {
|
|
WLOG(WS_LOG_DEBUG, "DEBUG MSG (%s): %s",
|
|
(lang == NULL) ? "none" : lang,
|
|
(msg == NULL) ? "no message" : msg);
|
|
}
|
|
|
|
*idx = begin;
|
|
|
|
WFREE(msg, ssh->ctx->heap, DYNTYPE_STRING);
|
|
WFREE(lang, ssh->ctx->heap, DYNTYPE_STRING);
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
static int DoUnimplemented(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
word32 seq;
|
|
word32 begin = *idx;
|
|
|
|
(void)ssh;
|
|
(void)len;
|
|
|
|
ato32(buf + begin, &seq);
|
|
begin += UINT32_SZ;
|
|
|
|
WLOG(WS_LOG_DEBUG, "UNIMPLEMENTED: seq %u", seq);
|
|
|
|
*idx = begin;
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
static int DoDisconnect(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
|
|
{
|
|
word32 reason;
|
|
const char* reasonStr = NULL;
|
|
word32 begin = *idx;
|
|
|
|
(void)ssh;
|
|
(void)len;
|
|
(void)reasonStr;
|
|
|
|
ato32(buf + begin, &reason);
|
|
begin += UINT32_SZ;
|
|
|
|
#ifdef NO_WOLFSSH_STRINGS
|
|
WLOG(WS_LOG_DEBUG, "DISCONNECT: (%u)", reason);
|
|
#elif defined(DEBUG_WOLFSSH)
|
|
switch (reason) {
|
|
case WOLFSSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT:
|
|
reasonStr = "host not allowed to connect"; break;
|
|
case WOLFSSH_DISCONNECT_PROTOCOL_ERROR:
|
|
reasonStr = "protocol error"; break;
|
|
case WOLFSSH_DISCONNECT_KEY_EXCHANGE_FAILED:
|
|
reasonStr = "key exchange failed"; break;
|
|
case WOLFSSH_DISCONNECT_RESERVED:
|
|
reasonStr = "reserved"; break;
|
|
case WOLFSSH_DISCONNECT_MAC_ERROR:
|
|
reasonStr = "mac error"; break;
|
|
case WOLFSSH_DISCONNECT_COMPRESSION_ERROR:
|
|
reasonStr = "compression error"; break;
|
|
case WOLFSSH_DISCONNECT_SERVICE_NOT_AVAILABLE:
|
|
reasonStr = "service not available"; break;
|
|
case WOLFSSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED:
|
|
reasonStr = "protocol version not supported"; break;
|
|
case WOLFSSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE:
|
|
reasonStr = "host key not verifiable"; break;
|
|
case WOLFSSH_DISCONNECT_CONNECTION_LOST:
|
|
reasonStr = "connection lost"; break;
|
|
case WOLFSSH_DISCONNECT_BY_APPLICATION:
|
|
reasonStr = "disconnect by application"; break;
|
|
case WOLFSSH_DISCONNECT_TOO_MANY_CONNECTIONS:
|
|
reasonStr = "too many connections"; break;
|
|
case WOLFSSH_DISCONNECT_AUTH_CANCELLED_BY_USER:
|
|
reasonStr = "auth cancelled by user"; break;
|
|
case WOLFSSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE:
|
|
reasonStr = "no more auth methods available"; break;
|
|
case WOLFSSH_DISCONNECT_ILLEGAL_USER_NAME:
|
|
reasonStr = "illegal user name"; break;
|
|
default:
|
|
reasonStr = "unknown reason";
|
|
}
|
|
WLOG(WS_LOG_DEBUG, "DISCONNECT: (%u) %s", reason, reasonStr);
|
|
#endif
|
|
|
|
*idx = begin;
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
static int DoServiceRequest(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
word32 begin = *idx;
|
|
word32 nameSz;
|
|
char serviceName[WOLFSSH_MAX_NAMESZ];
|
|
|
|
(void)len;
|
|
|
|
ato32(buf + begin, &nameSz);
|
|
begin += LENGTH_SZ;
|
|
|
|
if (begin + nameSz > len || nameSz >= WOLFSSH_MAX_NAMESZ) {
|
|
return WS_BUFFER_E;
|
|
}
|
|
|
|
WMEMCPY(serviceName, buf + begin, nameSz);
|
|
begin += nameSz;
|
|
serviceName[nameSz] = 0;
|
|
|
|
*idx = begin;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Requesting service: %s", serviceName);
|
|
ssh->clientState = CLIENT_USERAUTH_REQUEST_DONE;
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
static int DoServiceAccept(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
word32 begin = *idx;
|
|
word32 nameSz;
|
|
char serviceName[WOLFSSH_MAX_NAMESZ];
|
|
|
|
(void)len;
|
|
|
|
ato32(buf + begin, &nameSz);
|
|
begin += LENGTH_SZ;
|
|
|
|
if (begin + nameSz > len || nameSz >= WOLFSSH_MAX_NAMESZ) {
|
|
return WS_BUFFER_E;
|
|
}
|
|
|
|
WMEMCPY(serviceName, buf + begin, nameSz);
|
|
begin += nameSz;
|
|
serviceName[nameSz] = 0;
|
|
|
|
*idx = begin;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Accepted service: %s", serviceName);
|
|
ssh->serverState = SERVER_USERAUTH_REQUEST_DONE;
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
#ifdef WOLFSSH_ALLOW_USERAUTH_NONE
|
|
/* Utility for DoUserAuthRequest() */
|
|
static int DoUserAuthRequestNone(WOLFSSH* ssh, WS_UserAuthData* authData,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestNone()");
|
|
|
|
(void)len;
|
|
|
|
if (ssh == NULL || authData == NULL ||
|
|
buf == NULL || idx == NULL) {
|
|
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
authData->type = WOLFSSH_USERAUTH_NONE;
|
|
if (ssh->ctx->userAuthCb != NULL) {
|
|
WLOG(WS_LOG_DEBUG, "DUARN: Calling the userauth callback");
|
|
ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_NONE,
|
|
authData, ssh->userAuthCtx);
|
|
if (ret == WOLFSSH_USERAUTH_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "DUARN: none check successful");
|
|
ssh->clientState = CLIENT_USERAUTH_DONE;
|
|
ret = WS_SUCCESS;
|
|
}
|
|
else if (ret == WOLFSSH_USERAUTH_REJECTED) {
|
|
WLOG(WS_LOG_DEBUG, "DUARN: password rejected");
|
|
#ifndef NO_FAILURE_ON_REJECTED
|
|
ret = SendUserAuthFailure(ssh, 0);
|
|
if (ret == WS_SUCCESS)
|
|
ret = WS_USER_AUTH_E;
|
|
#else
|
|
ret = WS_USER_AUTH_E;
|
|
#endif
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "DUARN: none check failed, retry");
|
|
ret = SendUserAuthFailure(ssh, 0);
|
|
}
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "DUARN: No user auth callback");
|
|
ret = SendUserAuthFailure(ssh, 0);
|
|
if (ret == WS_SUCCESS)
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestNone(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Utility for DoUserAuthRequest() */
|
|
static int DoUserAuthRequestPassword(WOLFSSH* ssh, WS_UserAuthData* authData,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
word32 begin;
|
|
WS_UserAuthData_Password* pw = NULL;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestPassword()");
|
|
|
|
if (ssh == NULL || authData == NULL ||
|
|
buf == NULL || len == 0 || idx == NULL) {
|
|
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
pw = &authData->sf.password;
|
|
authData->type = WOLFSSH_USERAUTH_PASSWORD;
|
|
ret = GetBoolean(&pw->hasNewPassword, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&pw->passwordSz, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
pw->password = buf + begin;
|
|
begin += pw->passwordSz;
|
|
|
|
if (pw->hasNewPassword) {
|
|
/* Skip the password change. Maybe error out since we aren't
|
|
* supporting password changes at this time. */
|
|
ret = GetUint32(&pw->newPasswordSz, buf, len, &begin);
|
|
if (ret == WS_SUCCESS) {
|
|
pw->newPassword = buf + begin;
|
|
begin += pw->newPasswordSz;
|
|
}
|
|
}
|
|
else {
|
|
pw->newPassword = NULL;
|
|
pw->newPasswordSz = 0;
|
|
}
|
|
|
|
if (ssh->ctx->userAuthCb != NULL) {
|
|
WLOG(WS_LOG_DEBUG, "DUARPW: Calling the userauth callback");
|
|
ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PASSWORD,
|
|
authData, ssh->userAuthCtx);
|
|
if (ret == WOLFSSH_USERAUTH_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "DUARPW: password check successful");
|
|
ssh->clientState = CLIENT_USERAUTH_DONE;
|
|
ret = WS_SUCCESS;
|
|
}
|
|
else if (ret == WOLFSSH_USERAUTH_REJECTED) {
|
|
WLOG(WS_LOG_DEBUG, "DUARPW: password rejected");
|
|
#ifndef NO_FAILURE_ON_REJECTED
|
|
ret = SendUserAuthFailure(ssh, 0);
|
|
if (ret == WS_SUCCESS)
|
|
ret = WS_USER_AUTH_E;
|
|
#else
|
|
ret = WS_USER_AUTH_E;
|
|
#endif
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "DUARPW: password check failed, retry");
|
|
ret = SendUserAuthFailure(ssh, 0);
|
|
}
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "DUARPW: No user auth callback");
|
|
ret = SendUserAuthFailure(ssh, 0);
|
|
if (ret == WS_SUCCESS)
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
*idx = begin;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestPassword(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
#ifndef WOLFSSH_NO_RSA
|
|
/* Utility for DoUserAuthRequestPublicKey() */
|
|
/* returns negative for error, positive is size of digest. */
|
|
static int DoUserAuthRequestRsa(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
|
|
byte hashId, byte* digest, word32 digestSz)
|
|
{
|
|
enum wc_HashType enmhashId = (enum wc_HashType)hashId;
|
|
byte *checkDigest = NULL;
|
|
byte *encDigest = NULL;
|
|
int checkDigestSz;
|
|
const byte* publicKeyType;
|
|
word32 publicKeyTypeSz = 0;
|
|
const byte* n;
|
|
word32 nSz = 0;
|
|
const byte* e = NULL;
|
|
word32 eSz = 0;
|
|
word32 i = 0;
|
|
int ret = WS_SUCCESS;
|
|
RsaKey *key_ptr = NULL;
|
|
|
|
#ifndef WOLFSSH_SMALL_STACK
|
|
byte s_checkDigest[MAX_ENCODED_SIG_SZ];
|
|
#endif
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestRsa()");
|
|
|
|
if (ssh == NULL || ssh->ctx == NULL || pk == NULL || digest == NULL ||
|
|
digestSz == 0) {
|
|
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
checkDigest = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, ssh->ctx->heap,
|
|
DYNTYPE_BUFFER);
|
|
if (checkDigest == NULL)
|
|
ret = WS_MEMORY_E;
|
|
#else
|
|
checkDigest = s_checkDigest;
|
|
#endif
|
|
key_ptr = (RsaKey*)WMALLOC(sizeof(RsaKey), ssh->ctx->heap,
|
|
DYNTYPE_PUBKEY);
|
|
if (key_ptr == NULL)
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WMEMSET(checkDigest, 0, MAX_ENCODED_SIG_SZ);
|
|
ret = wc_InitRsaKey(key_ptr, ssh->ctx->heap);
|
|
if (ret == 0) {
|
|
ret = WS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/* First check that the public key's type matches the one we are
|
|
* expecting. */
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetSize(&publicKeyTypeSz, pk->publicKey, pk->publicKeySz, &i);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
publicKeyType = pk->publicKey + i;
|
|
i += publicKeyTypeSz;
|
|
if (publicKeyTypeSz != pk->publicKeyTypeSz &&
|
|
WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
|
|
|
|
WLOG(WS_LOG_DEBUG,
|
|
"Public Key's type does not match public key type");
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetSize(&eSz, pk->publicKey, pk->publicKeySz, &i);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
e = pk->publicKey + i;
|
|
i += eSz;
|
|
ret = GetSize(&nSz, pk->publicKey, pk->publicKeySz, &i);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
n = pk->publicKey + i;
|
|
|
|
ret = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, key_ptr);
|
|
if (ret != 0) {
|
|
WLOG(WS_LOG_DEBUG, "Could not decode public key");
|
|
ret = WS_CRYPTO_FAILED;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
i = 0;
|
|
/* First check that the signature's public key type matches the one
|
|
* we are expecting. */
|
|
ret = GetSize(&publicKeyTypeSz, pk->publicKey, pk->publicKeySz, &i);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
publicKeyType = pk->publicKey + i;
|
|
i += publicKeyTypeSz;
|
|
|
|
if (publicKeyTypeSz != pk->publicKeyTypeSz &&
|
|
WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
|
|
|
|
WLOG(WS_LOG_DEBUG,
|
|
"Signature's type does not match public key type");
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetSize(&nSz, pk->signature, pk->signatureSz, &i);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
n = pk->signature + i;
|
|
checkDigestSz = wc_RsaSSL_Verify(n, nSz, checkDigest,
|
|
MAX_ENCODED_SIG_SZ, key_ptr);
|
|
if (checkDigestSz <= 0) {
|
|
WLOG(WS_LOG_DEBUG, "Could not verify signature");
|
|
ret = WS_CRYPTO_FAILED;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
word32 encDigestSz;
|
|
volatile int compare;
|
|
volatile int sizeCompare;
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
encDigest = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, ssh->ctx->heap,
|
|
DYNTYPE_BUFFER);
|
|
if (encDigest == NULL)
|
|
ret = WS_MEMORY_E;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
#else
|
|
byte s_encDigest[MAX_ENCODED_SIG_SZ];
|
|
encDigest = s_encDigest;
|
|
#endif
|
|
{
|
|
WMEMSET(encDigest, 0, MAX_ENCODED_SIG_SZ);
|
|
encDigestSz = wc_EncodeSignature(encDigest, digest,
|
|
wc_HashGetDigestSize(enmhashId),
|
|
wc_HashGetOID(enmhashId));
|
|
|
|
compare = ConstantCompare(encDigest, checkDigest,
|
|
encDigestSz);
|
|
sizeCompare = encDigestSz != (word32)checkDigestSz;
|
|
|
|
if ((compare == 0) && (sizeCompare == 0))
|
|
ret = WS_SUCCESS;
|
|
else
|
|
ret = WS_RSA_E;
|
|
}
|
|
}
|
|
if (key_ptr != NULL) {
|
|
wc_FreeRsaKey(key_ptr);
|
|
WFREE(key_ptr, ssh->ctx->heap, DYNTYPE_PUBKEY);
|
|
}
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
if (checkDigest)
|
|
WFREE(checkDigest, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (encDigest)
|
|
WFREE(encDigest, ssh->ctx_heap, DYNTYPE_BUFFER);
|
|
#endif
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestRsa(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
#endif /* ! WOLFSSH_NO_RSA */
|
|
|
|
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
/* Utility for DoUserAuthRequestPublicKey() */
|
|
/* returns negative for error, positive is size of digest. */
|
|
static int DoUserAuthRequestEcc(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
|
|
byte hashId, byte* digest, word32 digestSz)
|
|
{
|
|
const byte* publicKeyType;
|
|
word32 publicKeyTypeSz = 0;
|
|
const byte* curveName;
|
|
word32 curveNameSz = 0;
|
|
const byte* q = NULL;
|
|
word32 sz, qSz;
|
|
word32 i = 0;
|
|
int ret = WS_SUCCESS;
|
|
mp_int *sig_r_ptr = NULL, *sig_s_ptr = NULL;
|
|
ecc_key *key_ptr = NULL;
|
|
#ifndef WOLFSSH_SMALL_STACK
|
|
mp_int sig_r, sig_s;
|
|
ecc_key s_key;
|
|
#endif
|
|
(void)hashId;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestEcc()");
|
|
|
|
if (ssh == NULL || ssh->ctx == NULL || pk == NULL || digest == NULL ||
|
|
digestSz == 0) {
|
|
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
key_ptr = (ecc_key*)WMALLOC(sizeof(ecc_key), ssh->ctx->heap,
|
|
DYNTYPE_PUBKEY);
|
|
sig_r_ptr = (mp_int*)WMALLOC(sizeof(mp_int), ssh->ctx->heap, DYNTYPE_MPINT);
|
|
sig_s_ptr = (mp_int*)WMALLOC(sizeof(mp_int), ssh->ctx->heap, DYNTYPE_MPINT);
|
|
if (key_ptr == NULL || sig_r_ptr == NULL || sig_s_ptr == NULL)
|
|
ret = WS_MEMORY_E;
|
|
#else
|
|
key_ptr = &s_key;
|
|
sig_r_ptr = &sig_r;
|
|
sig_s_ptr = &sig_s;
|
|
#endif
|
|
}
|
|
|
|
if (sig_r_ptr != NULL) {
|
|
if (mp_init(sig_r_ptr) != MP_OKAY) {
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
}
|
|
if (sig_s_ptr != NULL) {
|
|
if (mp_init(sig_s_ptr) != MP_OKAY) {
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
}
|
|
if (key_ptr != NULL) {
|
|
if (wc_ecc_init_ex(key_ptr, ssh->ctx->heap, INVALID_DEVID) != 0) {
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
}
|
|
|
|
/* First check that the public key's type matches the one we are
|
|
* expecting. */
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetSize(&publicKeyTypeSz, pk->publicKey, pk->publicKeySz, &i);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
publicKeyType = pk->publicKey + i;
|
|
i += publicKeyTypeSz;
|
|
if (publicKeyTypeSz != pk->publicKeyTypeSz &&
|
|
WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
|
|
|
|
WLOG(WS_LOG_DEBUG,
|
|
"Public Key's type does not match public key type");
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetSize(&curveNameSz, pk->publicKey, pk->publicKeySz, &i);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
curveName = pk->publicKey + i;
|
|
(void)curveName; /* Not used at the moment, hush the compiler. */
|
|
i += curveNameSz;
|
|
ret = GetSize(&qSz, pk->publicKey, pk->publicKeySz, &i);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
q = pk->publicKey + i;
|
|
i += qSz;
|
|
ret = wc_ecc_import_x963(q, qSz, key_ptr);
|
|
}
|
|
|
|
if (ret != 0) {
|
|
WLOG(WS_LOG_DEBUG, "Could not decode public key");
|
|
ret = WS_CRYPTO_FAILED;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
i = 0;
|
|
/* First check that the signature's public key type matches the one
|
|
* we are expecting. */
|
|
ret = GetSize(&publicKeyTypeSz, pk->signature, pk->signatureSz, &i);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
publicKeyType = pk->signature + i;
|
|
i += publicKeyTypeSz;
|
|
|
|
if (publicKeyTypeSz != pk->publicKeyTypeSz &&
|
|
WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
|
|
|
|
WLOG(WS_LOG_DEBUG,
|
|
"Signature's type does not match public key type");
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
/* Get the size of the signature blob. */
|
|
ret = GetSize(&sz, pk->signature, pk->signatureSz, &i);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
/* Get R and S. */
|
|
ret = GetSize(&sz, pk->signature, pk->signatureSz, &i);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = mp_read_unsigned_bin(sig_r_ptr, pk->signature + i, sz);
|
|
if (ret != 0)
|
|
ret = WS_PARSE_E;
|
|
else
|
|
ret = WS_SUCCESS;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
i += sz;
|
|
ret = GetSize(&sz, pk->signature, pk->signatureSz, &i);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = mp_read_unsigned_bin(sig_s_ptr, pk->signature + i, sz);
|
|
if (ret != 0)
|
|
ret = WS_PARSE_E;
|
|
else
|
|
ret = WS_SUCCESS;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
int status = 0;
|
|
|
|
ret = wc_ecc_verify_hash_ex(sig_r_ptr, sig_s_ptr, digest, digestSz, &status, key_ptr);
|
|
if (ret != 0) {
|
|
WLOG(WS_LOG_DEBUG, "Could not verify signature");
|
|
ret = WS_CRYPTO_FAILED;
|
|
}
|
|
else
|
|
ret = status ? WS_SUCCESS : WS_ECC_E;
|
|
}
|
|
|
|
if (key_ptr)
|
|
wc_ecc_free(key_ptr);
|
|
if (sig_r_ptr)
|
|
mp_clear(sig_r_ptr);
|
|
if (sig_s_ptr)
|
|
mp_clear(sig_s_ptr);
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
if (key_ptr)
|
|
WFREE(key_ptr, ssh->ctx->heap, DYNTYPE_PUBKEY);
|
|
if (sig_r_ptr)
|
|
WFREE(sig_r_ptr, ssh->ctx->heap, DYNTYPE_MPINT);
|
|
if (sig_s_ptr)
|
|
WFREE(sig_s_ptr, ssh->ctx->heap, DYNTYPE_MPINT);
|
|
#endif
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestEcc(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifdef WOLFSSH_CERTS
|
|
static int DoUserAuthRequestEccCert(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
|
|
byte hashId, byte* digest, word32 digestSz)
|
|
{
|
|
const byte* publicKeyType;
|
|
word32 publicKeyTypeSz = 0;
|
|
word32 i = 0;
|
|
word32 sz;
|
|
int ret = WS_SUCCESS;
|
|
mp_int *sig_r_ptr = NULL, *sig_s_ptr = NULL;
|
|
ecc_key *key_ptr = NULL;
|
|
#ifndef WOLFSSH_SMALL_STACK
|
|
mp_int sig_r, sig_s;
|
|
ecc_key s_key;
|
|
#endif
|
|
(void)hashId;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestEccCert()");
|
|
|
|
if (ssh == NULL || ssh->ctx == NULL || pk == NULL || digest == NULL ||
|
|
digestSz == 0) {
|
|
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
key_ptr = (ecc_key*)WMALLOC(sizeof(ecc_key), ssh->ctx->heap,
|
|
DYNTYPE_PUBKEY);
|
|
sig_r_ptr = (mp_int*)WMALLOC(sizeof(mp_int), ssh->ctx->heap, DYNTYPE_MPINT);
|
|
sig_s_ptr = (mp_int*)WMALLOC(sizeof(mp_int), ssh->ctx->heap, DYNTYPE_MPINT);
|
|
if (key_ptr == NULL || sig_r_ptr == NULL || sig_s_ptr == NULL)
|
|
ret = WS_MEMORY_E;
|
|
#else
|
|
key_ptr = &s_key;
|
|
sig_r_ptr = &sig_r;
|
|
sig_s_ptr = &sig_s;
|
|
#endif
|
|
}
|
|
|
|
#ifdef WOLFSSH_CERTS
|
|
if (ret == WS_SUCCESS) {
|
|
ret = wolfSSH_CERTMAN_VerifyCert_buffer(ssh->ctx->certMan,
|
|
pk->publicKey, pk->publicKeySz);
|
|
}
|
|
#endif /* WOLFSSH_CERTS */
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = wc_ecc_init_ex(key_ptr, ssh->ctx->heap, INVALID_DEVID);
|
|
if (ret == 0) {
|
|
ret = WS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
byte big[1024];
|
|
word32 bigSz = sizeof big;
|
|
DecodedCert cert;
|
|
|
|
wc_InitDecodedCert(&cert, pk->publicKey, pk->publicKeySz,
|
|
ssh->ctx->heap);
|
|
ret = wc_ParseCert(&cert, CA_TYPE, 0, NULL);
|
|
ret = wc_GetPubKeyDerFromCert(&cert, big, &bigSz);
|
|
if (ret == 0) {
|
|
word32 idx = 0;
|
|
ret = wc_EccPublicKeyDecode(big, &idx, key_ptr, bigSz);
|
|
}
|
|
wc_FreeDecodedCert(&cert);
|
|
}
|
|
|
|
if (ret != 0) {
|
|
WLOG(WS_LOG_DEBUG, "Could not decode public key");
|
|
ret = WS_CRYPTO_FAILED;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
i = 0;
|
|
/* First check that the signature's public key type matches the one
|
|
* we are expecting. */
|
|
ret = GetSize(&publicKeyTypeSz, pk->signature, pk->signatureSz, &i);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
publicKeyType = pk->signature + i;
|
|
i += publicKeyTypeSz;
|
|
(void)publicKeyType;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
/* Get the size of the signature blob. */
|
|
ret = GetSize(&sz, pk->signature, pk->signatureSz, &i);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
/* Get R and S. */
|
|
ret = GetSize(&sz, pk->signature, pk->signatureSz, &i);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (mp_init(sig_r_ptr) != MP_OKAY) {
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = mp_read_unsigned_bin(sig_r_ptr, pk->signature + i, sz);
|
|
if (ret != 0)
|
|
ret = WS_PARSE_E;
|
|
else
|
|
ret = WS_SUCCESS;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
i += sz;
|
|
ret = GetSize(&sz, pk->signature, pk->signatureSz, &i);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (mp_init(sig_s_ptr) != MP_OKAY) {
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = mp_read_unsigned_bin(sig_s_ptr, pk->signature + i, sz);
|
|
if (ret != 0)
|
|
ret = WS_PARSE_E;
|
|
else
|
|
ret = WS_SUCCESS;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
int status = 0;
|
|
|
|
ret = wc_ecc_verify_hash_ex(sig_r_ptr, sig_s_ptr, digest, digestSz, &status, key_ptr);
|
|
if (ret != 0) {
|
|
WLOG(WS_LOG_DEBUG, "Could not verify signature");
|
|
ret = WS_CRYPTO_FAILED;
|
|
}
|
|
else
|
|
ret = status ? WS_SUCCESS : WS_ECC_E;
|
|
}
|
|
|
|
if (key_ptr)
|
|
wc_ecc_free(key_ptr);
|
|
if (sig_r_ptr)
|
|
mp_clear(sig_r_ptr);
|
|
if (sig_s_ptr)
|
|
mp_clear(sig_s_ptr);
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
if (key_ptr)
|
|
WFREE(key_ptr, ssh->ctx->heap, DYNTYPE_PUBKEY);
|
|
if (sig_r_ptr)
|
|
WFREE(sig_r_ptr, ssh->ctx->heap, DYNTYPE_MPINT);
|
|
if (sig_s_ptr)
|
|
WFREE(sig_s_ptr, ssh->ctx->heap, DYNTYPE_MPINT);
|
|
#endif
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestEccCert(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA)
|
|
/* Utility for DoUserAuthRequest() */
|
|
static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
word32 begin;
|
|
WS_UserAuthData_PublicKey* pk = NULL;
|
|
int ret = WS_SUCCESS;
|
|
int authFailure = 0;
|
|
byte pkTypeId;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestPublicKey()");
|
|
|
|
if (ssh == NULL || authData == NULL ||
|
|
buf == NULL || len == 0 || idx == NULL) {
|
|
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
pk = &authData->sf.publicKey;
|
|
authData->type = WOLFSSH_USERAUTH_PUBLICKEY;
|
|
ret = GetBoolean(&pk->hasSignature, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetSize(&pk->publicKeyTypeSz, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
pk->publicKeyType = buf + begin;
|
|
begin += pk->publicKeyTypeSz;
|
|
|
|
pkTypeId = NameToId((char*)pk->publicKeyType, pk->publicKeyTypeSz);
|
|
if (pkTypeId == ID_UNKNOWN) {
|
|
WLOG(WS_LOG_DEBUG, "DUARPK: Unknown / Unsupported key type");
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetSize(&pk->publicKeySz, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
pk->publicKey = buf + begin;
|
|
begin += pk->publicKeySz;
|
|
#ifdef WOLFSSH_CERTS
|
|
if (pkTypeId == ID_X509V3_SSH_RSA ||
|
|
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP256 ||
|
|
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP384 ||
|
|
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP521) {
|
|
word32 l = 0, m = 0;
|
|
|
|
/* Skip the name */
|
|
ret = GetSize(&l, pk->publicKey, pk->publicKeySz, &m);
|
|
m += l;
|
|
/* Get the cert count */
|
|
ret = GetUint32(&l, pk->publicKey, pk->publicKeySz, &m);
|
|
ret = GetSize(&l, pk->publicKey, pk->publicKeySz, &m);
|
|
pk->publicKeySz = l;
|
|
pk->publicKey = pk->publicKey + m;
|
|
}
|
|
#endif /* WOLFSSH_CERTS */
|
|
|
|
if (pk->hasSignature) {
|
|
ret = GetSize(&pk->signatureSz, buf, len, &begin);
|
|
if (ret == WS_SUCCESS) {
|
|
pk->signature = buf + begin;
|
|
begin += pk->signatureSz;
|
|
}
|
|
}
|
|
else {
|
|
pk->signature = NULL;
|
|
pk->signatureSz = 0;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
*idx = begin;
|
|
|
|
if (ssh->ctx->userAuthCb != NULL) {
|
|
WLOG(WS_LOG_DEBUG, "DUARPK: Calling the userauth callback");
|
|
ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PUBLICKEY,
|
|
authData, ssh->userAuthCtx);
|
|
WLOG(WS_LOG_DEBUG, "DUARPK: callback result = %d", ret);
|
|
if (ret == WOLFSSH_USERAUTH_SUCCESS)
|
|
ret = WS_SUCCESS;
|
|
else if (ret == WOLFSSH_USERAUTH_INVALID_PUBLICKEY) {
|
|
WLOG(WS_LOG_DEBUG, "DUARPK: client key rejected");
|
|
ret = SendUserAuthFailure(ssh, 0);
|
|
authFailure = 1;
|
|
}
|
|
else {
|
|
ret = SendUserAuthFailure(ssh, 0);
|
|
authFailure = 1;
|
|
}
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "DUARPK: no userauth callback set");
|
|
ret = SendUserAuthFailure(ssh, 0);
|
|
authFailure = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS && !authFailure) {
|
|
if (pk->signature == NULL) {
|
|
WLOG(WS_LOG_DEBUG, "DUARPK: Send the PK OK");
|
|
ret = SendUserAuthPkOk(ssh, pk->publicKeyType, pk->publicKeyTypeSz,
|
|
pk->publicKey, pk->publicKeySz);
|
|
}
|
|
else {
|
|
wc_HashAlg hash;
|
|
byte digest[WC_MAX_DIGEST_SIZE];
|
|
word32 digestSz = 0;
|
|
enum wc_HashType hashId = WC_HASH_TYPE_SHA;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
hashId = HashForId(pkTypeId);
|
|
WMEMSET(digest, 0, sizeof(digest));
|
|
ret = wc_HashGetDigestSize(hashId);
|
|
if (ret > 0) {
|
|
digestSz = ret;
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
if (ret == 0)
|
|
ret = wc_HashInit(&hash, hashId);
|
|
|
|
if (ret == 0) {
|
|
c32toa(ssh->sessionIdSz, digest);
|
|
ret = wc_HashUpdate(&hash, hashId, digest, UINT32_SZ);
|
|
}
|
|
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&hash, hashId,
|
|
ssh->sessionId, ssh->sessionIdSz);
|
|
|
|
if (ret == 0) {
|
|
digest[0] = MSGID_USERAUTH_REQUEST;
|
|
ret = wc_HashUpdate(&hash, hashId, digest, MSG_ID_SZ);
|
|
}
|
|
|
|
/* The rest of the fields in the signature are already
|
|
* in the buffer. Just need to account for the sizes. */
|
|
if (ret == 0) {
|
|
word32 dataToSignSz;
|
|
|
|
dataToSignSz = authData->usernameSz +
|
|
authData->serviceNameSz +
|
|
authData->authNameSz + BOOLEAN_SZ +
|
|
pk->publicKeyTypeSz + pk->publicKeySz +
|
|
(UINT32_SZ * 5);
|
|
|
|
#ifdef WOLFSSH_CERTS
|
|
if (pkTypeId == ID_X509V3_SSH_RSA ||
|
|
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP256 ||
|
|
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP384 ||
|
|
pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP521) {
|
|
|
|
dataToSignSz += pk->publicKeyTypeSz + (UINT32_SZ * 4);
|
|
}
|
|
#endif /* WOLFSSH_CERTS */
|
|
|
|
ret = wc_HashUpdate(&hash, hashId,
|
|
pk->dataToSign, dataToSignSz);
|
|
}
|
|
if (ret == 0) {
|
|
ret = wc_HashFinal(&hash, hashId, digest);
|
|
|
|
if (ret != 0)
|
|
ret = WS_CRYPTO_FAILED;
|
|
else
|
|
ret = WS_SUCCESS;
|
|
}
|
|
wc_HashFree(&hash, hashId);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
switch (pkTypeId) {
|
|
#ifndef WOLFSSH_NO_RSA
|
|
case ID_SSH_RSA:
|
|
ret = DoUserAuthRequestRsa(ssh, pk,
|
|
hashId, digest, digestSz);
|
|
break;
|
|
#ifdef WOLFSSH_CERTS
|
|
case ID_X509V3_SSH_RSA:
|
|
ret = DoUserAuthRequestRsa(ssh, pk,
|
|
hashId, digest, digestSz);
|
|
break;
|
|
#endif
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
case ID_ECDSA_SHA2_NISTP256:
|
|
case ID_ECDSA_SHA2_NISTP384:
|
|
case ID_ECDSA_SHA2_NISTP521:
|
|
ret = DoUserAuthRequestEcc(ssh, pk,
|
|
hashId, digest, digestSz);
|
|
break;
|
|
#ifdef WOLFSSH_CERTS
|
|
case ID_X509V3_ECDSA_SHA2_NISTP256:
|
|
case ID_X509V3_ECDSA_SHA2_NISTP384:
|
|
case ID_X509V3_ECDSA_SHA2_NISTP521:
|
|
ret = DoUserAuthRequestEccCert(ssh, pk,
|
|
hashId, digest, digestSz);
|
|
break;
|
|
#endif
|
|
#endif
|
|
default:
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
}
|
|
|
|
if (ret != WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "DUARPK: signature compare failure : [%d]",
|
|
ret);
|
|
ret = SendUserAuthFailure(ssh, 0);
|
|
}
|
|
else {
|
|
ssh->clientState = CLIENT_USERAUTH_DONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestPublicKey(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
|
|
static int DoUserAuthRequest(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
word32 begin;
|
|
int ret = WS_SUCCESS;
|
|
byte authNameId;
|
|
WS_UserAuthData authData;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequest()");
|
|
|
|
if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
WMEMSET(&authData, 0, sizeof(authData));
|
|
ret = GetSize(&authData.usernameSz, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
authData.username = buf + begin;
|
|
begin += authData.usernameSz;
|
|
|
|
ret = GetUint32(&authData.serviceNameSz, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (authData.serviceNameSz > len - begin) {
|
|
ret = WS_BUFFER_E;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
authData.serviceName = buf + begin;
|
|
begin += authData.serviceNameSz;
|
|
|
|
ret = GetSize(&authData.authNameSz, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
authData.authName = buf + begin;
|
|
begin += authData.authNameSz;
|
|
authNameId = NameToId((char*)authData.authName, authData.authNameSz);
|
|
|
|
if (authNameId == ID_USERAUTH_PASSWORD)
|
|
ret = DoUserAuthRequestPassword(ssh, &authData, buf, len, &begin);
|
|
#if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA)
|
|
else if (authNameId == ID_USERAUTH_PUBLICKEY) {
|
|
authData.sf.publicKey.dataToSign = buf + *idx;
|
|
ret = DoUserAuthRequestPublicKey(ssh, &authData, buf, len, &begin);
|
|
}
|
|
#endif
|
|
#ifdef WOLFSSH_ALLOW_USERAUTH_NONE
|
|
else if (authNameId == ID_NONE) {
|
|
ret = DoUserAuthRequestNone(ssh, &authData, buf, len, &begin);
|
|
}
|
|
#endif
|
|
else {
|
|
WLOG(WS_LOG_DEBUG,
|
|
"invalid userauth type: %s", IdToName(authNameId));
|
|
ret = SendUserAuthFailure(ssh, 0);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = wolfSSH_SetUsernameRaw(ssh,
|
|
authData.username, authData.usernameSz);
|
|
}
|
|
|
|
*idx = begin;
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequest(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int DoUserAuthFailure(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
byte authList[3]; /* Should only ever be password, publickey, hostname */
|
|
word32 authListSz = 3;
|
|
byte partialSuccess;
|
|
byte authType = 0;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoUserAuthFailure()");
|
|
|
|
if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetNameList(authList, &authListSz, buf, len, idx);
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetBoolean(&partialSuccess, buf, len, idx);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
word32 i;
|
|
|
|
/* check authList to see if authId is there */
|
|
for (i = 0; i < authListSz; i++) {
|
|
word32 j;
|
|
for (j = 0; j < sizeof(ssh->supportedAuth); j++) {
|
|
if (authList[i] == ssh->supportedAuth[j]) {
|
|
switch(authList[i]) {
|
|
case ID_USERAUTH_PASSWORD:
|
|
authType |= WOLFSSH_USERAUTH_PASSWORD;
|
|
break;
|
|
#if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA)
|
|
case ID_USERAUTH_PUBLICKEY:
|
|
authType |= WOLFSSH_USERAUTH_PUBLICKEY;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* the auth type attempted was not in the list */
|
|
if (authType == 0) {
|
|
WLOG(WS_LOG_DEBUG, "Did not match any auth IDs in peers list");
|
|
ret = WS_USER_AUTH_E;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = SendUserAuthRequest(ssh, authType, 0);
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthFailure(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int DoUserAuthSuccess(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoUserAuthSuccess()");
|
|
|
|
/* This message does not have any payload. len should be 0. */
|
|
if (ssh == NULL || buf == NULL || len != 0 || idx == NULL) {
|
|
ret = WS_BAD_ARGUMENT;
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthSuccess(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
ssh->serverState = SERVER_USERAUTH_ACCEPT_DONE;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthSuccess(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int DoUserAuthBanner(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
|
|
{
|
|
char banner[80];
|
|
word32 bannerSz = sizeof(banner);
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoUserAuthBanner()");
|
|
|
|
if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetString(banner, &bannerSz, buf, len, idx);
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetSize(&bannerSz, buf, len, idx);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (ssh->ctx->showBanner) {
|
|
WLOG(WS_LOG_INFO, "%s", banner);
|
|
}
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthBanner(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifdef WOLFSSH_FWD
|
|
static int DoGlobalRequestFwd(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx, int wantReply, int isCancel)
|
|
{
|
|
word32 begin;
|
|
int ret = WS_SUCCESS;
|
|
char* bindAddr = NULL;
|
|
word32 bindPort;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoGlobalRequestFwd()");
|
|
|
|
if (ssh == NULL || buf == NULL || len == 0 || idx == NULL) {
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
WLOG(WS_LOG_INFO, "wantReply = %d, isCancel = %d", wantReply, isCancel);
|
|
ret = GetStringAlloc(ssh->ctx->heap, &bindAddr, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = GetUint32(&bindPort, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_INFO, "Requesting forwarding%s for address %s on port %u.",
|
|
isCancel ? " cancel" : "", bindAddr, bindPort);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS && wantReply) {
|
|
ret = SendGlobalRequestFwdSuccess(ssh, 1, bindPort);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (ssh->ctx->fwdCb) {
|
|
ret = ssh->ctx->fwdCb(isCancel ? WOLFSSH_FWD_REMOTE_CLEANUP :
|
|
WOLFSSH_FWD_REMOTE_SETUP,
|
|
ssh->fwdCbCtx, bindAddr, bindPort);
|
|
}
|
|
}
|
|
|
|
if (bindAddr != NULL)
|
|
WFREE(bindAddr, ssh->ctx->heap, DYNTYPE_STRING);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoGlobalRequestFwd(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int DoGlobalRequest(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
word32 begin;
|
|
int ret = WS_SUCCESS;
|
|
char name[80];
|
|
word32 nameSz = sizeof(name);
|
|
int globReqId = ID_UNKNOWN;
|
|
byte wantReply = 0;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoGlobalRequest()");
|
|
|
|
if (ssh == NULL || buf == NULL || len == 0 || idx == NULL) {
|
|
ret = WS_BAD_ARGUMENT;
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoGlobalRequest(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
ret = GetString(name, &nameSz, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "DGR: request name = %s", name);
|
|
globReqId = NameToId(name, nameSz);
|
|
ret = GetBoolean(&wantReply, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
switch (globReqId) {
|
|
#ifdef WOLFSSH_FWD
|
|
case ID_GLOBREQ_TCPIP_FWD:
|
|
ret = DoGlobalRequestFwd(ssh, buf, len, &begin, wantReply, 0);
|
|
wantReply = 0;
|
|
break;
|
|
case ID_GLOBREQ_TCPIP_FWD_CANCEL:
|
|
ret = DoGlobalRequestFwd(ssh, buf, len, &begin, wantReply, 1);
|
|
wantReply = 0;
|
|
break;
|
|
#endif
|
|
default:
|
|
if (ssh->ctx->globalReqCb != NULL) {
|
|
ret = ssh->ctx->globalReqCb(ssh, name, nameSz, wantReply,
|
|
(void *)ssh->globalReqCtx);
|
|
|
|
if (wantReply) {
|
|
ret = SendRequestSuccess(ssh, (ret == WS_SUCCESS));
|
|
}
|
|
}
|
|
else if (wantReply)
|
|
ret = SendRequestSuccess(ssh, 0);
|
|
/* response SSH_MSG_REQUEST_FAILURE to Keep-Alive.
|
|
* IETF:draft-ssh-global-requests */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
*idx += len;
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoGlobalRequest(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifdef WOLFSSH_FWD
|
|
static int DoChannelOpenForward(WOLFSSH* ssh,
|
|
char** host, word32* hostPort,
|
|
char** origin, word32* originPort,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
word32 begin;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoChannelOpenForward()");
|
|
|
|
if (idx == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
ret = GetStringAlloc(ssh->ctx->heap, host, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(hostPort, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetStringAlloc(ssh->ctx->heap, origin, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(originPort, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
*idx = begin;
|
|
WLOG(WS_LOG_INFO, " host = %s:%u", *host, *hostPort);
|
|
WLOG(WS_LOG_INFO, " origin = %s:%u", *origin, *originPort);
|
|
}
|
|
else {
|
|
WFREE(*host, ssh->ctx->heap, DYNTYPE_STRING);
|
|
WFREE(*origin, ssh->ctx->heap, DYNTYPE_STRING);
|
|
*host = NULL;
|
|
*origin = NULL;
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoChannelOpenForward(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSH_FWD */
|
|
|
|
|
|
static int DoChannelOpen(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
word32 begin;
|
|
word32 typeSz;
|
|
char type[32];
|
|
byte typeId = ID_UNKNOWN;
|
|
word32 peerChannelId;
|
|
word32 peerInitialWindowSz;
|
|
word32 peerMaxPacketSz;
|
|
#ifdef WOLFSSH_FWD
|
|
char* host = NULL;
|
|
char* origin = NULL;
|
|
word32 hostPort = 0, originPort = 0;
|
|
int isDirect = 0;
|
|
#endif /* WOLFSSH_FWD */
|
|
WOLFSSH_CHANNEL* newChannel = NULL;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoChannelOpen()");
|
|
|
|
if (idx == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
typeSz = sizeof(type);
|
|
ret = GetString(type, &typeSz, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&peerChannelId, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&peerInitialWindowSz, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&peerMaxPacketSz, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_INFO, " type = %s", type);
|
|
WLOG(WS_LOG_INFO, " peerChannelId = %u", peerChannelId);
|
|
WLOG(WS_LOG_INFO, " peerInitialWindowSz = %u", peerInitialWindowSz);
|
|
WLOG(WS_LOG_INFO, " peerMaxPacketSz = %u", peerMaxPacketSz);
|
|
|
|
typeId = NameToId(type, typeSz);
|
|
switch (typeId) {
|
|
case ID_CHANTYPE_SESSION:
|
|
break;
|
|
#ifdef WOLFSSH_FWD
|
|
case ID_CHANTYPE_TCPIP_DIRECT:
|
|
isDirect = 1;
|
|
NO_BREAK;
|
|
case ID_CHANTYPE_TCPIP_FORWARD:
|
|
ret = DoChannelOpenForward(ssh,
|
|
&host, &hostPort, &origin, &originPort,
|
|
buf, len, &begin);
|
|
break;
|
|
#endif /* WOLFSSH_FWD */
|
|
#ifdef WOLFSSH_AGENT
|
|
case ID_CHANTYPE_AUTH_AGENT:
|
|
WLOG(WS_LOG_INFO, "agent = %p", ssh->agent);
|
|
if (ssh->agent != NULL)
|
|
ssh->agent->channel = peerChannelId;
|
|
else
|
|
ret = WS_AGENT_NULL_E;
|
|
break;
|
|
#endif
|
|
default:
|
|
ret = WS_INVALID_CHANTYPE;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
*idx = begin;
|
|
|
|
newChannel = ChannelNew(ssh, typeId,
|
|
ssh->ctx->windowSz, ssh->ctx->maxPacketSz);
|
|
if (newChannel == NULL)
|
|
ret = WS_RESOURCE_E;
|
|
else {
|
|
ChannelUpdatePeer(newChannel, peerChannelId,
|
|
peerInitialWindowSz, peerMaxPacketSz);
|
|
if (ssh->channelListSz == 0)
|
|
ssh->defaultPeerChannelId = peerChannelId;
|
|
#ifdef WOLFSSH_FWD
|
|
if (typeId == ID_CHANTYPE_TCPIP_DIRECT) {
|
|
ChannelUpdateForward(newChannel,
|
|
host, hostPort, origin, originPort, isDirect);
|
|
|
|
if (ssh->ctx->fwdCb) {
|
|
ret = ssh->ctx->fwdCb(WOLFSSH_FWD_LOCAL_SETUP,
|
|
ssh->fwdCbCtx, host, hostPort);
|
|
if (ret == WS_SUCCESS) {
|
|
ret = ssh->ctx->fwdCb(WOLFSSH_FWD_CHANNEL_ID,
|
|
ssh->fwdCbCtx, NULL, newChannel->channel);
|
|
}
|
|
}
|
|
}
|
|
#endif /* WOLFSSH_FWD */
|
|
ChannelAppend(ssh, newChannel);
|
|
|
|
ssh->clientState = CLIENT_CHANNEL_OPEN_DONE;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = SendChannelOpenConf(ssh, newChannel);
|
|
|
|
#ifdef WOLFSSH_FWD
|
|
/* ChannelUpdateForward makes new host and origin buffer */
|
|
WFREE(host, ssh->ctx->heap, DYNTYPE_STRING);
|
|
WFREE(origin, ssh->ctx->heap, DYNTYPE_STRING);
|
|
#endif /* WOLFSSH_FWD */
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoChannelOpen(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int DoChannelOpenConf(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
WOLFSSH_CHANNEL* channel;
|
|
word32 begin, channelId, peerChannelId,
|
|
peerInitialWindowSz, peerMaxPacketSz;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoChannelOpenConf()");
|
|
|
|
if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
ret = GetUint32(&channelId, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&peerChannelId, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&peerInitialWindowSz, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&peerMaxPacketSz, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_INFO, " channelId = %u", channelId);
|
|
WLOG(WS_LOG_INFO, " peerChannelId = %u", peerChannelId);
|
|
WLOG(WS_LOG_INFO, " peerInitialWindowSz = %u", peerInitialWindowSz);
|
|
WLOG(WS_LOG_INFO, " peerMaxPacketSz = %u", peerMaxPacketSz);
|
|
|
|
channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF);
|
|
if (channel == NULL)
|
|
ret = WS_INVALID_CHANID;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = ChannelUpdatePeer(channel, peerChannelId,
|
|
peerInitialWindowSz, peerMaxPacketSz);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ssh->serverState = SERVER_CHANNEL_OPEN_DONE;
|
|
ssh->defaultPeerChannelId = peerChannelId;
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoChannelOpenConf(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int DoChannelOpenFail(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
char desc[80];
|
|
word32 begin, channelId, reasonId, descSz, langSz;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoChannelOpenFail()");
|
|
|
|
if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
ret = GetUint32(&channelId, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&reasonId, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
descSz = sizeof(desc);
|
|
ret = GetString(desc, &descSz, buf, len, &begin);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetSize(&langSz, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
*idx = begin + langSz;
|
|
|
|
WLOG(WS_LOG_INFO, "channel open failure reason code: %u", reasonId);
|
|
if (descSz > 0) {
|
|
WLOG(WS_LOG_INFO, "description: %s", desc);
|
|
}
|
|
|
|
ret = ChannelRemove(ssh, channelId, WS_CHANNEL_ID_SELF);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = WS_CHANOPEN_FAILED;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoChannelOpenFail(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int DoChannelEof(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
WOLFSSH_CHANNEL* channel = NULL;
|
|
word32 begin = *idx;
|
|
word32 channelId;
|
|
int ret;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoChannelEof()");
|
|
|
|
ret = GetUint32(&channelId, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
*idx = begin;
|
|
|
|
channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF);
|
|
if (channel == NULL)
|
|
ret = WS_INVALID_CHANID;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
channel->eofRxd = 1;
|
|
if (!channel->eofTxd) {
|
|
ret = SendChannelEof(ssh, channel->peerChannel);
|
|
}
|
|
ssh->lastRxId = channelId;
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoChannelEof(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int DoChannelClose(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
WOLFSSH_CHANNEL* channel = NULL;
|
|
word32 begin = *idx;
|
|
word32 channelId;
|
|
int ret;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoChannelClose()");
|
|
|
|
ret = GetUint32(&channelId, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
*idx = begin;
|
|
|
|
channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF);
|
|
if (channel == NULL)
|
|
ret = WS_INVALID_CHANID;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (!channel->closeTxd) {
|
|
ret = SendChannelClose(ssh, channel->peerChannel);
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = ChannelRemove(ssh, channelId, WS_CHANNEL_ID_SELF);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = WS_CHANNEL_CLOSED;
|
|
ssh->lastRxId = channelId;
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoChannelClose(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int DoChannelRequest(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
WOLFSSH_CHANNEL* channel = NULL;
|
|
word32 begin = *idx;
|
|
word32 channelId;
|
|
word32 typeSz;
|
|
char type[32];
|
|
byte wantReply;
|
|
int ret;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoChannelRequest()");
|
|
|
|
ret = GetUint32(&channelId, buf, len, &begin);
|
|
|
|
typeSz = sizeof(type);
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetString(type, &typeSz, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetBoolean(&wantReply, buf, len, &begin);
|
|
|
|
if (ret != WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoChannelRequest(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF);
|
|
if (channel == NULL)
|
|
ret = WS_INVALID_CHANID;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, " channelId = %u", channelId);
|
|
WLOG(WS_LOG_DEBUG, " type = %s", type);
|
|
WLOG(WS_LOG_DEBUG, " wantReply = %u", wantReply);
|
|
|
|
#ifdef WOLFSSH_TERM
|
|
if (WSTRNCMP(type, "pty-req", typeSz) == 0) {
|
|
char term[32];
|
|
word32 termSz;
|
|
word32 widthChar, heightRows, widthPixels, heightPixels;
|
|
word32 modesSz;
|
|
|
|
termSz = sizeof(term);
|
|
ret = GetString(term, &termSz, buf, len, &begin);
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&widthChar, buf, len, &begin);
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&heightRows, buf, len, &begin);
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&widthPixels, buf, len, &begin);
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&heightPixels, buf, len, &begin);
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&modesSz, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, " term = %s", term);
|
|
WLOG(WS_LOG_DEBUG, " widthChar = %u", widthChar);
|
|
WLOG(WS_LOG_DEBUG, " heightRows = %u", heightRows);
|
|
WLOG(WS_LOG_DEBUG, " widthPixels = %u", widthPixels);
|
|
WLOG(WS_LOG_DEBUG, " heightPixels = %u", heightPixels);
|
|
WLOG(WS_LOG_DEBUG, " modes = %u", (modesSz - 1) / 5);
|
|
}
|
|
}
|
|
else
|
|
#endif /* WOLFSSH_TERM */
|
|
if (WSTRNCMP(type, "env", typeSz) == 0) {
|
|
char name[WOLFSSH_MAX_NAMESZ];
|
|
word32 nameSz;
|
|
char value[32];
|
|
word32 valueSz;
|
|
|
|
name[0] = 0;
|
|
value[0] = 0;
|
|
nameSz = sizeof(name);
|
|
valueSz = sizeof(value);
|
|
ret = GetString(name, &nameSz, buf, len, &begin);
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetString(value, &valueSz, buf, len, &begin);
|
|
|
|
WLOG(WS_LOG_DEBUG, " %s = %s", name, value);
|
|
}
|
|
else if (WSTRNCMP(type, "shell", typeSz) == 0) {
|
|
channel->sessionType = WOLFSSH_SESSION_SHELL;
|
|
ssh->clientState = CLIENT_DONE;
|
|
}
|
|
else if (WSTRNCMP(type, "exec", typeSz) == 0) {
|
|
ret = GetStringAlloc(ssh->ctx->heap, &channel->command,
|
|
buf, len, &begin);
|
|
channel->sessionType = WOLFSSH_SESSION_EXEC;
|
|
ssh->clientState = CLIENT_DONE;
|
|
|
|
WLOG(WS_LOG_DEBUG, " command = %s", channel->command);
|
|
}
|
|
else if (WSTRNCMP(type, "subsystem", typeSz) == 0) {
|
|
ret = GetStringAlloc(ssh->ctx->heap, &channel->command,
|
|
buf, len, &begin);
|
|
channel->sessionType = WOLFSSH_SESSION_SUBSYSTEM;
|
|
ssh->clientState = CLIENT_DONE;
|
|
|
|
WLOG(WS_LOG_DEBUG, " subsystem = %s", channel->command);
|
|
}
|
|
#ifdef WOLFSSH_AGENT
|
|
else if (WSTRNCMP(type, "auth-agent-req@openssh.com", typeSz) == 0) {
|
|
WLOG(WS_LOG_AGENT, " ssh-agent");
|
|
if (ssh->ctx->agentCb != NULL)
|
|
ssh->useAgent = 1;
|
|
else
|
|
WLOG(WS_LOG_AGENT, "Agent callback not set, not using.");
|
|
}
|
|
#endif /* WOLFSSH_AGENT */
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
*idx = len;
|
|
|
|
if (wantReply) {
|
|
int replyRet;
|
|
|
|
replyRet = SendChannelSuccess(ssh, channelId, (ret == WS_SUCCESS));
|
|
if (replyRet != WS_SUCCESS)
|
|
ret = replyRet;
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoChannelRequest(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int DoChannelSuccess(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoChannelSuccess()");
|
|
|
|
if (ssh == NULL || buf == NULL || len == 0 || idx == NULL) {
|
|
ret = WS_BAD_ARGUMENT;
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoChannelSuccess(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
ssh->serverState = SERVER_DONE;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoChannelSuccess(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int DoChannelFailure(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoChannelFailure()");
|
|
|
|
if (ssh == NULL || buf == NULL || len != 0 || idx == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = WS_CHANOPEN_FAILED;
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoChannelFailure(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int DoChannelWindowAdjust(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
WOLFSSH_CHANNEL* channel = NULL;
|
|
word32 begin = *idx;
|
|
word32 channelId, bytesToAdd;
|
|
int ret;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoChannelWindowAdjust()");
|
|
|
|
ret = GetUint32(&channelId, buf, len, &begin);
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&bytesToAdd, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
*idx = begin;
|
|
|
|
channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF);
|
|
if (channel == NULL)
|
|
ret = WS_INVALID_CHANID;
|
|
else {
|
|
WLOG(WS_LOG_INFO, " channelId = %u", channelId);
|
|
WLOG(WS_LOG_INFO, " bytesToAdd = %u", bytesToAdd);
|
|
WLOG(WS_LOG_INFO, " peerWindowSz = %u",
|
|
channel->peerWindowSz);
|
|
|
|
channel->peerWindowSz += bytesToAdd;
|
|
|
|
WLOG(WS_LOG_INFO, " update peerWindowSz = %u",
|
|
channel->peerWindowSz);
|
|
|
|
}
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoChannelWindowAdjust(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int DoChannelData(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
WOLFSSH_CHANNEL* channel = NULL;
|
|
word32 begin = *idx;
|
|
word32 dataSz = 0;
|
|
word32 channelId;
|
|
int ret;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoChannelData()");
|
|
|
|
ret = GetUint32(&channelId, buf, len, &begin);
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetSize(&dataSz, buf, len, &begin);
|
|
|
|
/* Validate dataSz */
|
|
if (ret == WS_SUCCESS) {
|
|
if (len < begin) {
|
|
ret = WS_RECV_OVERFLOW_E;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
*idx = begin + dataSz;
|
|
|
|
channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF);
|
|
if (channel == NULL)
|
|
ret = WS_INVALID_CHANID;
|
|
else
|
|
ret = ChannelPutData(channel, buf + begin, dataSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ssh->lastRxId = channelId;
|
|
ret = WS_CHAN_RXD;
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoChannelData(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* deletes current buffer and updates it
|
|
* return WS_SUCCESS on success */
|
|
static int PutBuffer(Buffer* buf, byte* data, word32 dataSz)
|
|
{
|
|
int ret;
|
|
|
|
/* reset "used" section of buffer back to 0 */
|
|
buf->length = 0;
|
|
buf->idx = 0;
|
|
|
|
if (dataSz > buf->bufferSz) {
|
|
if ((ret = GrowBuffer(buf, dataSz, 0)) != WS_SUCCESS) {
|
|
return ret;
|
|
}
|
|
}
|
|
WMEMCPY(buf->buffer, data, dataSz);
|
|
buf->length = dataSz;
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
static int DoChannelExtendedData(WOLFSSH* ssh,
|
|
byte* buf, word32 len, word32* idx)
|
|
{
|
|
WOLFSSH_CHANNEL* channel = NULL;
|
|
word32 begin = *idx;
|
|
word32 dataSz = 0;
|
|
word32 channelId;
|
|
word32 dataTypeCode;
|
|
int ret;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering DoChannelExtendedData()");
|
|
|
|
ret = GetUint32(&channelId, buf, len, &begin);
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetUint32(&dataTypeCode, buf, len, &begin);
|
|
if (ret == WS_SUCCESS)
|
|
ret = (dataTypeCode == CHANNEL_EXTENDED_DATA_STDERR) ?
|
|
WS_SUCCESS : WS_INVALID_EXTDATA;
|
|
if (ret == WS_SUCCESS)
|
|
ret = GetSize(&dataSz, buf, len, &begin);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF);
|
|
if (channel == NULL)
|
|
ret = WS_INVALID_CHANID;
|
|
else {
|
|
ret = PutBuffer(&ssh->extDataBuffer, buf + begin, dataSz);
|
|
#ifdef DEBUG_WOLFSSH
|
|
DumpOctetString(buf + begin, dataSz);
|
|
#endif
|
|
if (ret == WS_SUCCESS) {
|
|
ret = SendChannelWindowAdjust(ssh, channel->channel, dataSz);
|
|
}
|
|
}
|
|
*idx = begin + dataSz;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ssh->lastRxId = channelId;
|
|
ret = WS_EXTDATA;
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving DoChannelExtendedData(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int DoPacket(WOLFSSH* ssh)
|
|
{
|
|
byte* buf = (byte*)ssh->inputBuffer.buffer;
|
|
word32 idx = ssh->inputBuffer.idx;
|
|
word32 len = ssh->inputBuffer.length;
|
|
word32 payloadSz;
|
|
byte padSz;
|
|
byte msg;
|
|
word32 payloadIdx = 0;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "DoPacket sequence number: %d", ssh->peerSeq);
|
|
|
|
idx += LENGTH_SZ;
|
|
padSz = buf[idx++];
|
|
|
|
/* check for underflow */
|
|
if ((word32)(PAD_LENGTH_SZ + padSz + MSG_ID_SZ) > ssh->curSz) {
|
|
return WS_OVERFLOW_E;
|
|
}
|
|
|
|
payloadSz = ssh->curSz - PAD_LENGTH_SZ - padSz - MSG_ID_SZ;
|
|
|
|
msg = buf[idx++];
|
|
/* At this point, payload starts at "buf + idx". */
|
|
|
|
/* sanity check on payloadSz. Uses "or" condition because of the case when
|
|
* adding idx to payloadSz causes it to overflow.
|
|
*/
|
|
if ((ssh->inputBuffer.bufferSz < payloadSz + idx) ||
|
|
(payloadSz + idx < payloadSz)) {
|
|
return WS_OVERFLOW_E;
|
|
}
|
|
|
|
switch (msg) {
|
|
|
|
case MSGID_DISCONNECT:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_DISCONNECT");
|
|
ret = DoDisconnect(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_IGNORE:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_IGNORE");
|
|
ret = DoIgnore(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_UNIMPLEMENTED:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_UNIMPLEMENTED");
|
|
ret = DoUnimplemented(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_REQUEST_SUCCESS:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_REQUEST_SUCCESS");
|
|
ret = DoRequestSuccess(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_REQUEST_FAILURE:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_REQUEST_FAILURE");
|
|
ret = DoRequestFailure(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_DEBUG:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_DEBUG");
|
|
ret = DoDebug(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_KEXINIT:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXINIT");
|
|
ret = DoKexInit(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
if (ssh->isKeying == 1 &&
|
|
ssh->connectState == CONNECT_SERVER_CHANNEL_REQUEST_DONE) {
|
|
if (ssh->handshake->kexId == ID_DH_GEX_SHA256) {
|
|
#if !defined(WOLFSSH_NO_DH) && !defined(WOLFSSH_NO_DH_GEX_SHA256)
|
|
ssh->error = SendKexDhGexRequest(ssh);
|
|
#endif
|
|
}
|
|
else
|
|
ssh->error = SendKexDhInit(ssh);
|
|
}
|
|
break;
|
|
|
|
case MSGID_NEWKEYS:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_NEWKEYS");
|
|
ret = DoNewKeys(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_KEXDH_INIT:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_INIT");
|
|
ret = DoKexDhInit(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_KEXDH_REPLY:
|
|
if (ssh->handshake == NULL) {
|
|
ret = WS_MEMORY_E;
|
|
break;
|
|
}
|
|
|
|
if (ssh->handshake->kexId == ID_DH_GEX_SHA256) {
|
|
#ifndef WOLFSSH_NO_DH_GEX_SHA256
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_GEX_GROUP");
|
|
ret = DoKexDhGexGroup(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
#endif
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_REPLY");
|
|
ret = DoKexDhReply(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
}
|
|
break;
|
|
|
|
#ifndef WOLFSSH_NO_DH_GEX_SHA256
|
|
case MSGID_KEXDH_GEX_REQUEST:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_GEX_REQUEST");
|
|
ret = DoKexDhGexRequest(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
#endif
|
|
|
|
case MSGID_KEXDH_GEX_INIT:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_GEX_INIT");
|
|
ret = DoKexDhInit(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_KEXDH_GEX_REPLY:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_GEX_INIT");
|
|
ret = DoKexDhReply(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_SERVICE_REQUEST:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_SERVICE_REQUEST");
|
|
ret = DoServiceRequest(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_SERVICE_ACCEPT:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_SERVER_ACCEPT");
|
|
ret = DoServiceAccept(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_USERAUTH_REQUEST:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_USERAUTH_REQUEST");
|
|
ret = DoUserAuthRequest(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_USERAUTH_FAILURE:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_USERAUTH_FAILURE");
|
|
ret = DoUserAuthFailure(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_USERAUTH_SUCCESS:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_USERAUTH_SUCCESS");
|
|
ret = DoUserAuthSuccess(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_USERAUTH_BANNER:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_USERAUTH_BANNER");
|
|
ret = DoUserAuthBanner(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_GLOBAL_REQUEST:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_GLOBAL_REQUEST");
|
|
ret = DoGlobalRequest(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_CHANNEL_OPEN:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_OPEN");
|
|
ret = DoChannelOpen(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_CHANNEL_OPEN_CONF:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_OPEN_CONF");
|
|
ret = DoChannelOpenConf(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_CHANNEL_OPEN_FAIL:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_OPEN_FAIL");
|
|
ret = DoChannelOpenFail(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_CHANNEL_WINDOW_ADJUST:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_WINDOW_ADJUST");
|
|
ret = DoChannelWindowAdjust(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_CHANNEL_DATA:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_DATA");
|
|
ret = DoChannelData(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_CHANNEL_EXTENDED_DATA:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_EXTENDED_DATA");
|
|
ret = DoChannelExtendedData(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_CHANNEL_EOF:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_EOF");
|
|
ret = DoChannelEof(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_CHANNEL_CLOSE:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_CLOSE");
|
|
ret = DoChannelClose(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_CHANNEL_REQUEST:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_REQUEST");
|
|
ret = DoChannelRequest(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_CHANNEL_SUCCESS:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_SUCCESS");
|
|
ret = DoChannelSuccess(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
case MSGID_CHANNEL_FAILURE:
|
|
WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_FAILURE");
|
|
ret = DoChannelFailure(ssh, buf + idx, payloadSz, &payloadIdx);
|
|
break;
|
|
|
|
default:
|
|
WLOG(WS_LOG_DEBUG, "Unimplemented message ID (%d)", msg);
|
|
#ifdef SHOW_UNIMPLEMENTED
|
|
DumpOctetString(buf + idx, payloadSz);
|
|
#endif
|
|
ret = SendUnimplemented(ssh);
|
|
}
|
|
|
|
if (payloadSz > 0) {
|
|
idx += payloadIdx;
|
|
if (idx + padSz > len) {
|
|
WLOG(WS_LOG_DEBUG, "Not enough data in buffer for pad.");
|
|
ret = WS_BUFFER_E;
|
|
}
|
|
}
|
|
|
|
idx += padSz;
|
|
ssh->inputBuffer.idx = idx;
|
|
ssh->peerSeq++;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifndef WOLFSSH_NO_AES_CTR
|
|
#if defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION == 2)
|
|
/*
|
|
* The FIPSv2 version of wc_AesCtrEncrypt() only works if the input and
|
|
* output are different buffers. This helper copies each block into a
|
|
* scratch buffer, then calling the AesCtrEncrypt() function on the
|
|
* single scratch buffer. But, only in FIPS builds.
|
|
*/
|
|
static INLINE int AesCtrEncryptHelper(Aes* aes,
|
|
byte* out, const byte* in, word32 sz)
|
|
{
|
|
int ret = 0;
|
|
byte scratch[AES_BLOCK_SIZE];
|
|
|
|
if (aes == NULL || in == NULL || out == NULL || sz == 0)
|
|
return WS_BAD_ARGUMENT;
|
|
|
|
if (sz % AES_BLOCK_SIZE)
|
|
return WS_ENCRYPT_E;
|
|
|
|
while (ret == 0 && sz) {
|
|
XMEMCPY(scratch, in, AES_BLOCK_SIZE);
|
|
ret = wc_AesCtrEncrypt(aes, out, scratch, AES_BLOCK_SIZE);
|
|
out += AES_BLOCK_SIZE;
|
|
in += AES_BLOCK_SIZE;
|
|
sz -= AES_BLOCK_SIZE;
|
|
}
|
|
ForceZero(scratch, sizeof(scratch));
|
|
|
|
return ret;
|
|
}
|
|
#define AESCTRHELPER(a,b,c,d) AesCtrEncryptHelper((a),(b),(c),(d))
|
|
#else
|
|
#define AESCTRHELPER(a,b,c,d) wc_AesCtrEncrypt((a),(b),(c),(d))
|
|
#endif
|
|
#endif
|
|
|
|
|
|
static INLINE int Encrypt(WOLFSSH* ssh, byte* cipher, const byte* input,
|
|
word16 sz)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL || cipher == NULL || input == NULL || sz == 0)
|
|
return WS_BAD_ARGUMENT;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Encrypt %s", IdToName(ssh->encryptId));
|
|
|
|
switch (ssh->encryptId) {
|
|
case ID_NONE:
|
|
break;
|
|
|
|
#ifndef WOLFSSH_NO_AES_CBC
|
|
case ID_AES128_CBC:
|
|
case ID_AES192_CBC:
|
|
case ID_AES256_CBC:
|
|
if (sz % AES_BLOCK_SIZE || wc_AesCbcEncrypt(&ssh->encryptCipher.aes,
|
|
cipher, input, sz) < 0) {
|
|
|
|
ret = WS_ENCRYPT_E;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifndef WOLFSSH_NO_AES_CTR
|
|
case ID_AES128_CTR:
|
|
case ID_AES192_CTR:
|
|
case ID_AES256_CTR:
|
|
if (sz % AES_BLOCK_SIZE || AESCTRHELPER(&ssh->encryptCipher.aes,
|
|
cipher, input, sz) < 0) {
|
|
|
|
ret = WS_ENCRYPT_E;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
|
|
ssh->txCount += sz;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static INLINE int Decrypt(WOLFSSH* ssh, byte* plain, const byte* input,
|
|
word16 sz)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL || plain == NULL || input == NULL || sz == 0)
|
|
return WS_BAD_ARGUMENT;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Decrypt %s", IdToName(ssh->peerEncryptId));
|
|
|
|
switch (ssh->peerEncryptId) {
|
|
case ID_NONE:
|
|
break;
|
|
|
|
#ifndef WOLFSSH_NO_AES_CBC
|
|
case ID_AES128_CBC:
|
|
case ID_AES192_CBC:
|
|
case ID_AES256_CBC:
|
|
if (sz % AES_BLOCK_SIZE || wc_AesCbcDecrypt(&ssh->decryptCipher.aes,
|
|
plain, input, sz) < 0) {
|
|
|
|
ret = WS_DECRYPT_E;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifndef WOLFSSH_NO_AES_CTR
|
|
case ID_AES128_CTR:
|
|
case ID_AES192_CTR:
|
|
case ID_AES256_CTR:
|
|
if (sz % AES_BLOCK_SIZE || AESCTRHELPER(&ssh->decryptCipher.aes,
|
|
plain, input, sz) < 0) {
|
|
|
|
ret = WS_DECRYPT_E;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
|
|
ssh->rxCount += sz;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = HighwaterCheck(ssh, WOLFSSH_HWSIDE_RECEIVE);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static INLINE int CreateMac(WOLFSSH* ssh, const byte* in, word32 inSz,
|
|
byte* mac)
|
|
{
|
|
byte flatSeq[LENGTH_SZ];
|
|
int ret;
|
|
|
|
WMEMSET(flatSeq, 0, LENGTH_SZ);
|
|
c32toa(ssh->seq, flatSeq);
|
|
|
|
WLOG(WS_LOG_DEBUG, "CreateMac %s", IdToName(ssh->macId));
|
|
|
|
/* Need to MAC the sequence number and the unencrypted packet */
|
|
switch (ssh->macId) {
|
|
case ID_NONE:
|
|
ret = WS_SUCCESS;
|
|
break;
|
|
|
|
#ifndef WOLFSSH_NO_HMAC_SHA1_96
|
|
case ID_HMAC_SHA1_96:
|
|
{
|
|
Hmac hmac;
|
|
byte digest[WC_SHA_DIGEST_SIZE];
|
|
|
|
ret = wc_HmacInit(&hmac, ssh->ctx->heap, INVALID_DEVID);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacSetKey(&hmac, WC_SHA,
|
|
ssh->keys.macKey, ssh->keys.macKeySz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacUpdate(&hmac, flatSeq, sizeof(flatSeq));
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacUpdate(&hmac, in, inSz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacFinal(&hmac, digest);
|
|
if (ret == WS_SUCCESS)
|
|
WMEMCPY(mac, digest, SHA1_96_SZ);
|
|
wc_HmacFree(&hmac);
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifndef WOLFSSH_NO_HMAC_SHA1
|
|
case ID_HMAC_SHA1:
|
|
{
|
|
Hmac hmac;
|
|
|
|
ret = wc_HmacInit(&hmac, ssh->ctx->heap, INVALID_DEVID);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacSetKey(&hmac, WC_SHA,
|
|
ssh->keys.macKey, ssh->keys.macKeySz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacUpdate(&hmac, flatSeq, sizeof(flatSeq));
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacUpdate(&hmac, in, inSz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacFinal(&hmac, mac);
|
|
wc_HmacFree(&hmac);
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case ID_HMAC_SHA2_256:
|
|
{
|
|
Hmac hmac;
|
|
|
|
ret = wc_HmacInit(&hmac, ssh->ctx->heap, INVALID_DEVID);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacSetKey(&hmac, WC_SHA256,
|
|
ssh->keys.macKey,
|
|
ssh->keys.macKeySz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacUpdate(&hmac, flatSeq, sizeof(flatSeq));
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacUpdate(&hmac, in, inSz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacFinal(&hmac, mac);
|
|
wc_HmacFree(&hmac);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
WLOG(WS_LOG_DEBUG, "Invalid Mac ID");
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static INLINE int VerifyMac(WOLFSSH* ssh, const byte* in, word32 inSz,
|
|
const byte* mac)
|
|
{
|
|
int ret;
|
|
byte flatSeq[LENGTH_SZ];
|
|
byte checkMac[MAX_HMAC_SZ];
|
|
Hmac hmac;
|
|
|
|
c32toa(ssh->peerSeq, flatSeq);
|
|
|
|
WLOG(WS_LOG_DEBUG, "VerifyMac %s", IdToName(ssh->peerMacId));
|
|
WLOG(WS_LOG_DEBUG, "VM: inSz = %u", inSz);
|
|
WLOG(WS_LOG_DEBUG, "VM: seq = %u", ssh->peerSeq);
|
|
WLOG(WS_LOG_DEBUG, "VM: keyLen = %u", ssh->peerKeys.macKeySz);
|
|
|
|
WMEMSET(checkMac, 0, sizeof(checkMac));
|
|
ret = wc_HmacInit(&hmac, ssh->ctx->heap, INVALID_DEVID);
|
|
if (ret != WS_SUCCESS) {
|
|
WLOG(WS_LOG_ERROR, "VM: Error initializing hmac structure");
|
|
}
|
|
else {
|
|
switch (ssh->peerMacId) {
|
|
case ID_NONE:
|
|
ret = WS_SUCCESS;
|
|
break;
|
|
|
|
case ID_HMAC_SHA1:
|
|
case ID_HMAC_SHA1_96:
|
|
ret = wc_HmacSetKey(&hmac, WC_SHA, ssh->peerKeys.macKey,
|
|
ssh->peerKeys.macKeySz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacUpdate(&hmac, flatSeq, sizeof(flatSeq));
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacUpdate(&hmac, in, inSz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacFinal(&hmac, checkMac);
|
|
if (ConstantCompare(checkMac, mac, ssh->peerMacSz) != 0)
|
|
ret = WS_VERIFY_MAC_E;
|
|
break;
|
|
|
|
case ID_HMAC_SHA2_256:
|
|
ret = wc_HmacSetKey(&hmac, WC_SHA256, ssh->peerKeys.macKey,
|
|
ssh->peerKeys.macKeySz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacUpdate(&hmac, flatSeq, sizeof(flatSeq));
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacUpdate(&hmac, in, inSz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HmacFinal(&hmac, checkMac);
|
|
if (ConstantCompare(checkMac, mac, ssh->peerMacSz) != 0)
|
|
ret = WS_VERIFY_MAC_E;
|
|
break;
|
|
|
|
default:
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
wc_HmacFree(&hmac);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifndef WOLFSSH_NO_AEAD
|
|
static INLINE void AeadIncrementExpIv(byte* iv)
|
|
{
|
|
int i;
|
|
|
|
iv += AEAD_IMP_IV_SZ;
|
|
|
|
for (i = AEAD_EXP_IV_SZ-1; i >= 0; i--) {
|
|
if (++iv[i]) return;
|
|
}
|
|
}
|
|
|
|
|
|
static INLINE int EncryptAead(WOLFSSH* ssh, byte* cipher,
|
|
const byte* input, word16 sz,
|
|
byte* authTag, const byte* auth,
|
|
word16 authSz)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL || cipher == NULL || input == NULL || sz == 0 ||
|
|
authTag == NULL || auth == NULL || authSz == 0)
|
|
return WS_BAD_ARGUMENT;
|
|
|
|
WLOG(WS_LOG_DEBUG, "EncryptAead %s", IdToName(ssh->encryptId));
|
|
|
|
switch (ssh->encryptId) {
|
|
#ifndef WOLFSSH_NO_AES_GCM
|
|
case ID_AES128_GCM:
|
|
case ID_AES192_GCM:
|
|
case ID_AES256_GCM:
|
|
ret = wc_AesGcmEncrypt(&ssh->encryptCipher.aes, cipher, input, sz,
|
|
ssh->keys.iv, ssh->keys.ivSz,
|
|
authTag, ssh->macSz, auth, authSz);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
|
|
AeadIncrementExpIv(ssh->keys.iv);
|
|
ssh->txCount += sz;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static INLINE int DecryptAead(WOLFSSH* ssh, byte* plain,
|
|
const byte* input, word16 sz,
|
|
const byte* authTag, const byte* auth,
|
|
word16 authSz)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL || plain == NULL || input == NULL || sz == 0 ||
|
|
authTag == NULL || auth == NULL || authSz == 0)
|
|
return WS_BAD_ARGUMENT;
|
|
|
|
WLOG(WS_LOG_DEBUG, "DecryptAead %s", IdToName(ssh->peerEncryptId));
|
|
|
|
switch (ssh->peerEncryptId) {
|
|
#ifndef WOLFSSH_NO_AES_GCM
|
|
case ID_AES128_GCM:
|
|
case ID_AES192_GCM:
|
|
case ID_AES256_GCM:
|
|
ret = wc_AesGcmDecrypt(&ssh->decryptCipher.aes, plain, input, sz,
|
|
ssh->peerKeys.iv, ssh->peerKeys.ivSz,
|
|
authTag, ssh->peerMacSz, auth, authSz);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
|
|
AeadIncrementExpIv(ssh->peerKeys.iv);
|
|
ssh->rxCount += sz;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = HighwaterCheck(ssh, WOLFSSH_HWSIDE_RECEIVE);
|
|
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSH_NO_AEAD */
|
|
|
|
|
|
int DoReceive(WOLFSSH* ssh)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
int verifyResult;
|
|
word32 readSz;
|
|
byte peerBlockSz = ssh->peerBlockSz;
|
|
byte peerMacSz = ssh->peerMacSz;
|
|
byte aeadMode = ssh->peerAeadMode;
|
|
|
|
switch (ssh->processReplyState) {
|
|
case PROCESS_INIT:
|
|
readSz = peerBlockSz;
|
|
WLOG(WS_LOG_DEBUG, "PR1: size = %u", readSz);
|
|
if ((ret = GetInputData(ssh, readSz)) < 0) {
|
|
return ret;
|
|
}
|
|
ssh->processReplyState = PROCESS_PACKET_LENGTH;
|
|
|
|
if (!aeadMode) {
|
|
/* Decrypt first block if encrypted */
|
|
ret = Decrypt(ssh,
|
|
ssh->inputBuffer.buffer +
|
|
ssh->inputBuffer.idx,
|
|
ssh->inputBuffer.buffer +
|
|
ssh->inputBuffer.idx,
|
|
readSz);
|
|
if (ret != WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "PR: First decrypt fail");
|
|
ssh->error = ret;
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
}
|
|
NO_BREAK;
|
|
|
|
case PROCESS_PACKET_LENGTH:
|
|
if (ssh->inputBuffer.idx + UINT32_SZ >
|
|
ssh->inputBuffer.bufferSz) {
|
|
ssh->error = WS_OVERFLOW_E;
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
/* Peek at the packet_length field. */
|
|
ato32(ssh->inputBuffer.buffer + ssh->inputBuffer.idx,
|
|
&ssh->curSz);
|
|
if (ssh->curSz >
|
|
MAX_PACKET_SZ - (word32)peerMacSz - LENGTH_SZ) {
|
|
ssh->error = WS_OVERFLOW_E;
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
ssh->processReplyState = PROCESS_PACKET_FINISH;
|
|
NO_BREAK;
|
|
|
|
case PROCESS_PACKET_FINISH:
|
|
readSz = ssh->curSz + LENGTH_SZ + peerMacSz;
|
|
WLOG(WS_LOG_DEBUG, "PR2: size = %u", readSz);
|
|
if (readSz > 0) {
|
|
if ((ret = GetInputData(ssh, readSz)) < 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (!aeadMode) {
|
|
if (ssh->curSz + LENGTH_SZ - peerBlockSz > 0) {
|
|
ret = Decrypt(ssh,
|
|
ssh->inputBuffer.buffer +
|
|
ssh->inputBuffer.idx + peerBlockSz,
|
|
ssh->inputBuffer.buffer +
|
|
ssh->inputBuffer.idx + peerBlockSz,
|
|
ssh->curSz + LENGTH_SZ - peerBlockSz);
|
|
}
|
|
else {
|
|
/* Entire packet fit in one block, don't need
|
|
* to decrypt any more data this packet. */
|
|
}
|
|
|
|
/* Verify the buffer is big enough for the data and mac.
|
|
* Even if the decrypt step fails, verify the MAC anyway.
|
|
* This keeps consistent timing. */
|
|
verifyResult = VerifyMac(ssh,
|
|
ssh->inputBuffer.buffer +
|
|
ssh->inputBuffer.idx,
|
|
ssh->curSz + LENGTH_SZ,
|
|
ssh->inputBuffer.buffer +
|
|
ssh->inputBuffer.idx +
|
|
LENGTH_SZ + ssh->curSz);
|
|
if (ret != WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "PR: Decrypt fail");
|
|
ssh->error = ret;
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
if (verifyResult != WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "PR: VerifyMac fail");
|
|
ssh->error = verifyResult;
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
#ifndef WOLFSSH_NO_AEAD
|
|
ret = DecryptAead(ssh,
|
|
ssh->inputBuffer.buffer +
|
|
ssh->inputBuffer.idx +
|
|
LENGTH_SZ,
|
|
ssh->inputBuffer.buffer +
|
|
ssh->inputBuffer.idx +
|
|
LENGTH_SZ,
|
|
ssh->curSz,
|
|
ssh->inputBuffer.buffer +
|
|
ssh->inputBuffer.idx +
|
|
ssh->curSz + LENGTH_SZ,
|
|
ssh->inputBuffer.buffer +
|
|
ssh->inputBuffer.idx,
|
|
LENGTH_SZ);
|
|
|
|
if (ret != WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "PR: DecryptAead fail");
|
|
ssh->error = ret;
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
ssh->processReplyState = PROCESS_PACKET;
|
|
NO_BREAK;
|
|
|
|
case PROCESS_PACKET:
|
|
ret = DoPacket(ssh);
|
|
ssh->error = ret;
|
|
if (ret < 0 && !(ret == WS_CHAN_RXD || ret == WS_EXTDATA ||
|
|
ret == WS_CHANNEL_CLOSED || ret == WS_WANT_WRITE)) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
WLOG(WS_LOG_DEBUG, "PR3: peerMacSz = %u", peerMacSz);
|
|
ssh->inputBuffer.idx += peerMacSz;
|
|
break;
|
|
|
|
default:
|
|
WLOG(WS_LOG_DEBUG, "Bad process input state, program error");
|
|
ssh->error = WS_INPUT_CASE_E;
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
WLOG(WS_LOG_DEBUG, "PR4: Shrinking input buffer");
|
|
ShrinkBuffer(&ssh->inputBuffer, 1);
|
|
ssh->processReplyState = PROCESS_INIT;
|
|
|
|
WLOG(WS_LOG_DEBUG, "PR5: txCount = %u, rxCount = %u",
|
|
ssh->txCount, ssh->rxCount);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int DoProtoId(WOLFSSH* ssh)
|
|
{
|
|
int ret;
|
|
word32 idSz;
|
|
byte* eol;
|
|
byte SSH_PROTO_EOL_SZ = 1;
|
|
|
|
if ( (ret = GetInputText(ssh, &eol)) < 0) {
|
|
WLOG(WS_LOG_DEBUG, "get input text failed");
|
|
return ret;
|
|
}
|
|
|
|
if (eol == NULL) {
|
|
WLOG(WS_LOG_DEBUG, "invalid EOL");
|
|
return WS_VERSION_E;
|
|
}
|
|
|
|
if (WSTRNCASECMP((char*)ssh->inputBuffer.buffer,
|
|
sshProtoIdStr, SSH_PROTO_SZ) == 0) {
|
|
|
|
if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER)
|
|
ssh->clientState = CLIENT_VERSION_DONE;
|
|
else
|
|
ssh->serverState = SERVER_VERSION_DONE;
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "SSH version mismatch");
|
|
return WS_VERSION_E;
|
|
}
|
|
if (WSTRNCMP((char*)ssh->inputBuffer.buffer,
|
|
OpenSSH, sizeof(OpenSSH)-1) == 0) {
|
|
ssh->clientOpenSSH = 1;
|
|
}
|
|
|
|
if (*eol == '\r') {
|
|
SSH_PROTO_EOL_SZ++;
|
|
}
|
|
*eol = 0;
|
|
|
|
idSz = (word32)WSTRLEN((char*)ssh->inputBuffer.buffer);
|
|
|
|
/* Store the proto ID for later use. It is used in keying and rekeying. */
|
|
ssh->peerProtoId = (byte*)WMALLOC(idSz + LENGTH_SZ,
|
|
ssh->ctx->heap, DYNTYPE_STRING);
|
|
if (ssh->peerProtoId == NULL)
|
|
ret = WS_MEMORY_E;
|
|
else {
|
|
c32toa(idSz, ssh->peerProtoId);
|
|
WMEMCPY(ssh->peerProtoId + LENGTH_SZ, ssh->inputBuffer.buffer, idSz);
|
|
ssh->peerProtoIdSz = idSz + LENGTH_SZ;
|
|
}
|
|
|
|
ssh->inputBuffer.idx += idSz + SSH_PROTO_EOL_SZ;
|
|
ShrinkBuffer(&ssh->inputBuffer, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendProtoId(WOLFSSH* ssh)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
word32 sshProtoIdStrSz;
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "%s", sshProtoIdStr);
|
|
sshProtoIdStrSz = (word32)WSTRLEN(sshProtoIdStr);
|
|
ret = GrowBuffer(&ssh->outputBuffer, sshProtoIdStrSz, 0);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WMEMCPY(ssh->outputBuffer.buffer, sshProtoIdStr, sshProtoIdStrSz);
|
|
ssh->outputBuffer.length = sshProtoIdStrSz;
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* payloadSz is an estimate. It should be a worst case. The actual value
|
|
* will be nailed down when the packet is bundled to be sent. */
|
|
static int PreparePacket(WOLFSSH* ssh, word32 payloadSz)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (ssh->outputBuffer.length < ssh->outputBuffer.idx)
|
|
ret = WS_OVERFLOW_E;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
word32 packetSz, usedSz, outputSz;
|
|
byte paddingSz;
|
|
|
|
paddingSz = ssh->blockSz * 2;
|
|
packetSz = PAD_LENGTH_SZ + payloadSz + paddingSz;
|
|
outputSz = LENGTH_SZ + packetSz + ssh->macSz;
|
|
usedSz = ssh->outputBuffer.length - ssh->outputBuffer.idx;
|
|
|
|
ret = GrowBuffer(&ssh->outputBuffer, outputSz, usedSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ssh->packetStartIdx = ssh->outputBuffer.length;
|
|
ssh->outputBuffer.length += LENGTH_SZ + PAD_LENGTH_SZ;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int BundlePacket(WOLFSSH* ssh)
|
|
{
|
|
byte* output = NULL;
|
|
word32 idx = 0;
|
|
byte paddingSz = 0;
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
word32 payloadSz, packetSz;
|
|
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
/* Calculate the actual payload size based on the data
|
|
* written into the buffer. packetStartIdx is before the
|
|
* LENGTH and PAD_LENGTH, subtract those out, as well. */
|
|
payloadSz = idx - ssh->packetStartIdx - LENGTH_SZ - PAD_LENGTH_SZ;
|
|
|
|
/* Minimum value for paddingSz is 4. */
|
|
paddingSz = ssh->blockSz -
|
|
((ssh->aeadMode ? 0 : LENGTH_SZ) +
|
|
PAD_LENGTH_SZ + payloadSz) % ssh->blockSz;
|
|
if (paddingSz < MIN_PAD_LENGTH)
|
|
paddingSz += ssh->blockSz;
|
|
|
|
packetSz = PAD_LENGTH_SZ + payloadSz + paddingSz;
|
|
|
|
/* fill in the packetSz, paddingSz */
|
|
c32toa(packetSz, output + ssh->packetStartIdx);
|
|
output[ssh->packetStartIdx + LENGTH_SZ] = paddingSz;
|
|
|
|
/* end new */
|
|
|
|
/* Add the padding */
|
|
WLOG(WS_LOG_DEBUG, "BP: paddingSz = %u", paddingSz);
|
|
if (ssh->encryptId == ID_NONE)
|
|
WMEMSET(output + idx, 0, paddingSz);
|
|
else if (wc_RNG_GenerateBlock(ssh->rng, output + idx, paddingSz) < 0) {
|
|
ret = WS_CRYPTO_FAILED;
|
|
WLOG(WS_LOG_DEBUG, "BP: failed to add padding");
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (!ssh->aeadMode) {
|
|
byte macSz = MacSzForId(ssh->macId);
|
|
|
|
idx += paddingSz;
|
|
|
|
WMEMSET(output + idx, 0, macSz);
|
|
if (idx + macSz > ssh->outputBuffer.bufferSz) {
|
|
ret = WS_BUFFER_E;
|
|
}
|
|
else {
|
|
ret = CreateMac(ssh, ssh->outputBuffer.buffer +
|
|
ssh->packetStartIdx, ssh->outputBuffer.length -
|
|
ssh->packetStartIdx + paddingSz, output + idx);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
idx += ssh->macSz;
|
|
ret = Encrypt(ssh,
|
|
ssh->outputBuffer.buffer + ssh->packetStartIdx,
|
|
ssh->outputBuffer.buffer + ssh->packetStartIdx,
|
|
ssh->outputBuffer.length -
|
|
ssh->packetStartIdx + paddingSz);
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "BP: failed to generate mac");
|
|
}
|
|
}
|
|
else {
|
|
#ifndef WOLFSSH_NO_AEAD
|
|
idx += paddingSz;
|
|
ret = EncryptAead(ssh,
|
|
ssh->outputBuffer.buffer + ssh->packetStartIdx + LENGTH_SZ,
|
|
ssh->outputBuffer.buffer + ssh->packetStartIdx + LENGTH_SZ,
|
|
ssh->outputBuffer.length - ssh->packetStartIdx + paddingSz
|
|
- LENGTH_SZ,
|
|
output + idx,
|
|
ssh->outputBuffer.buffer + ssh->packetStartIdx,
|
|
LENGTH_SZ);
|
|
idx += ssh->macSz;
|
|
#else
|
|
ret = WS_INVALID_ALGO_ID;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ssh->seq++;
|
|
ssh->outputBuffer.length = idx;
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "BP: failed to encrypt buffer");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void PurgePacket(WOLFSSH* ssh)
|
|
{
|
|
if (ssh != NULL) {
|
|
ssh->packetStartIdx = 0;
|
|
ssh->outputBuffer.idx = 0;
|
|
ssh->outputBuffer.plainSz = 0;
|
|
ShrinkBuffer(&ssh->outputBuffer, 1);
|
|
}
|
|
}
|
|
|
|
|
|
static INLINE void CopyNameList(byte* buf, word32* idx,
|
|
const char* src, word32 srcSz)
|
|
{
|
|
word32 begin = *idx;
|
|
|
|
c32toa(srcSz, buf + begin);
|
|
begin += LENGTH_SZ;
|
|
WMEMCPY(buf + begin, src, srcSz);
|
|
begin += srcSz;
|
|
|
|
*idx = begin;
|
|
}
|
|
|
|
static const char cannedEncAlgoNames[] =
|
|
#if !defined(WOLFSSH_NO_AES_GCM)
|
|
"aes256-gcm@openssh.com,"
|
|
"aes192-gcm@openssh.com,"
|
|
"aes128-gcm@openssh.com,"
|
|
#endif
|
|
#if !defined(WOLFSSH_NO_AES_CTR)
|
|
"aes256-ctr,"
|
|
"aes192-ctr,"
|
|
"aes128-ctr,"
|
|
#endif
|
|
#if !defined(WOLFSSH_NO_AES_CBC)
|
|
"aes256-cbc,"
|
|
"aes192-cbc,"
|
|
"aes128-cbc,"
|
|
#endif
|
|
"";
|
|
#if defined(WOLFSSH_NO_AES_GCM) && defined(WOLFSSH_NO_AES_CTR) && \
|
|
defined(WOLFSSH_NO_AES_CBC)
|
|
#warning "You need at least one encryption algorithm."
|
|
#endif
|
|
|
|
static const char cannedMacAlgoNames[] =
|
|
#if !defined(WOLFSSH_NO_HMAC_SHA2_256)
|
|
"hmac-sha2-256,"
|
|
#endif
|
|
#if !defined(WOLFSSH_NO_HMAC_SHA1_96)
|
|
"hmac-sha1-96,"
|
|
#endif
|
|
#if !defined(WOLFSSH_NO_HMAC_SHA1)
|
|
"hmac-sha1,"
|
|
#endif
|
|
"";
|
|
#if defined(WOLFSSH_NO_HMAC_SHA2_256) && \
|
|
defined(WOLFSSH_NO_HMAC_SHA1_96) && \
|
|
defined(WOLFSSH_NO_HMAC_SHA1)
|
|
#warning "You need at least one MAC algorithm."
|
|
#endif
|
|
|
|
static const char cannedKeyAlgoClientNames[] =
|
|
#ifdef WOLFSSH_CERTS
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
|
|
"x509v3-ecdsa-sha2-nistp521,"
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
|
|
"x509v3-ecdsa-sha2-nistp384,"
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
|
"x509v3-ecdsa-sha2-nistp256,"
|
|
#endif
|
|
#ifndef WOLFSSH_NO_SSH_RSA_SHA1
|
|
"x509v3-ssh-rsa,"
|
|
#endif
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
|
|
"ecdsa-sha2-nistp521,"
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
|
|
"ecdsa-sha2-nistp384,"
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
|
"ecdsa-sha2-nistp256,"
|
|
#endif
|
|
#ifndef WOLFSSH_NO_SSH_RSA_SHA1
|
|
"ssh-rsa,"
|
|
#endif
|
|
"";
|
|
#if defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && \
|
|
defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && \
|
|
defined(WOLFSSH_NO_ECDSA_SHA2_NISTP521) && \
|
|
defined(WOLFSSH_NO_SSH_RSA_SHA1)
|
|
#warning "You need at least one signing algorithm."
|
|
#endif
|
|
|
|
static const char cannedKeyAlgoRsaNames[] = "ssh-rsa";
|
|
#ifdef WOLFSSH_CERTS
|
|
static const char cannedKeyAlgoX509RsaNames[] = "x509v3-ssh-rsa";
|
|
#endif
|
|
#if !defined(WOLFSSH_NO_ECDSA) && !defined(WOLFSSH_NO_ECDH)
|
|
static const char cannedKeyAlgoEcc256Names[] = "ecdsa-sha2-nistp256";
|
|
static const char cannedKeyAlgoEcc384Names[] = "ecdsa-sha2-nistp384";
|
|
static const char cannedKeyAlgoEcc521Names[] = "ecdsa-sha2-nistp521";
|
|
#ifdef WOLFSSH_CERTS
|
|
static const char cannedKeyAlgoX509Ecc256Names[] = "x509v3-ecdsa-sha2-nistp256";
|
|
static const char cannedKeyAlgoX509Ecc384Names[] = "x509v3-ecdsa-sha2-nistp384";
|
|
static const char cannedKeyAlgoX509Ecc521Names[] = "x509v3-ecdsa-sha2-nistp521";
|
|
#endif
|
|
#endif
|
|
|
|
static const char cannedKexAlgoNames[] =
|
|
#if !defined(WOLFSSH_NO_SABER_LEVEL1_SHA256)
|
|
"saber-lightsaber-sha256,"
|
|
#endif
|
|
#if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP521)
|
|
"ecdh-sha2-nistp521,"
|
|
#endif
|
|
#if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP384)
|
|
"ecdh-sha2-nistp384,"
|
|
#endif
|
|
#if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP256)
|
|
"ecdh-sha2-nistp256,"
|
|
#endif
|
|
#if !defined(WOLFSSH_NO_DH_GEX_SHA256)
|
|
"diffie-hellman-group-exchange-sha256,"
|
|
#endif
|
|
#if !defined(WOLFSSH_NO_DH_GROUP14_SHA1)
|
|
"diffie-hellman-group14-sha1,"
|
|
#endif
|
|
#if !defined(WOLFSSH_NO_DH_GROUP1_SHA1)
|
|
"diffie-hellman-group1-sha1,"
|
|
#endif
|
|
"";
|
|
|
|
#if defined(WOLFSSH_NO_ECDH_SHA2_NISTP256) && \
|
|
defined(WOLFSSH_NO_DH_GEX_SHA256) && \
|
|
defined(WOLFSSH_NO_DH_GROUP14_SHA1) && \
|
|
defined(WOLFSSH_NO_DH_GROUP1_SHA1) && \
|
|
defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) && \
|
|
defined(WOLFSSH_NO_ECDH_SHA2_NISTP384) && \
|
|
defined(WOLFSSH_NO_SABER_LEVEL1_256)
|
|
#warning "You need at least one key exchange algorithm."
|
|
#endif
|
|
|
|
static const char cannedNoneNames[] = "none";
|
|
|
|
/* -1 for the null, some are -1 for the comma */
|
|
static const word32 cannedEncAlgoNamesSz = sizeof(cannedEncAlgoNames) - 2;
|
|
static const word32 cannedMacAlgoNamesSz = sizeof(cannedMacAlgoNames) - 2;
|
|
static const word32 cannedKeyAlgoClientNamesSz =
|
|
sizeof(cannedKeyAlgoClientNames) - 2;
|
|
static const word32 cannedKeyAlgoRsaNamesSz = sizeof(cannedKeyAlgoRsaNames) - 1;
|
|
#ifdef WOLFSSH_CERTS
|
|
static const word32 cannedKeyAlgoX509RsaNamesSz =
|
|
sizeof(cannedKeyAlgoX509RsaNames) - 1;
|
|
#endif
|
|
#if !defined(WOLFSSH_NO_ECDSA) && !defined(WOLFSSH_NO_ECDH)
|
|
static const word32 cannedKeyAlgoEcc256NamesSz =
|
|
sizeof(cannedKeyAlgoEcc256Names) - 1;
|
|
static const word32 cannedKeyAlgoEcc384NamesSz =
|
|
sizeof(cannedKeyAlgoEcc384Names) - 1;
|
|
static const word32 cannedKeyAlgoEcc521NamesSz =
|
|
sizeof(cannedKeyAlgoEcc521Names) - 1;
|
|
#ifdef WOLFSSH_CERTS
|
|
static const word32 cannedKeyAlgoX509Ecc256NamesSz =
|
|
sizeof(cannedKeyAlgoX509Ecc256Names) - 1;
|
|
static const word32 cannedKeyAlgoX509Ecc384NamesSz =
|
|
sizeof(cannedKeyAlgoX509Ecc384Names) - 1;
|
|
static const word32 cannedKeyAlgoX509Ecc521NamesSz =
|
|
sizeof(cannedKeyAlgoX509Ecc521Names) - 1;
|
|
#endif
|
|
#endif
|
|
static const word32 cannedKexAlgoNamesSz = sizeof(cannedKexAlgoNames) - 2;
|
|
static const word32 cannedNoneNamesSz = sizeof(cannedNoneNames) - 1;
|
|
|
|
|
|
int SendKexInit(WOLFSSH* ssh)
|
|
{
|
|
byte* output = NULL;
|
|
byte* payload = NULL;
|
|
word32 idx = 0;
|
|
word32 payloadSz = 0;
|
|
int ret = WS_SUCCESS;
|
|
const char* cannedKeyAlgoNames = NULL;
|
|
word32 cannedKeyAlgoNamesSz = 0;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendKexInit()");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ssh->isKeying = 1;
|
|
if (ssh->handshake == NULL) {
|
|
ssh->handshake = HandshakeInfoNew(ssh->ctx->heap);
|
|
if (ssh->handshake == NULL) {
|
|
WLOG(WS_LOG_DEBUG, "Couldn't allocate handshake info");
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) {
|
|
switch (ssh->ctx->useEcc) {
|
|
#if !defined(WOLFSSH_NO_ECDSA) && !defined(WOLFSSH_NO_ECDH)
|
|
case ECC_SECP256R1:
|
|
if (ssh->ctx->useCert) {
|
|
#ifdef WOLFSSH_CERTS
|
|
cannedKeyAlgoNames = cannedKeyAlgoX509Ecc256Names;
|
|
cannedKeyAlgoNamesSz =
|
|
cannedKeyAlgoX509Ecc256NamesSz;
|
|
#endif
|
|
}
|
|
else {
|
|
cannedKeyAlgoNames = cannedKeyAlgoEcc256Names;
|
|
cannedKeyAlgoNamesSz = cannedKeyAlgoEcc256NamesSz;
|
|
}
|
|
break;
|
|
case ECC_SECP384R1:
|
|
if (ssh->ctx->useCert) {
|
|
#ifdef WOLFSSH_CERTS
|
|
cannedKeyAlgoNames = cannedKeyAlgoX509Ecc384Names;
|
|
cannedKeyAlgoNamesSz =
|
|
cannedKeyAlgoX509Ecc384NamesSz;
|
|
#endif
|
|
}
|
|
else {
|
|
cannedKeyAlgoNames = cannedKeyAlgoEcc384Names;
|
|
cannedKeyAlgoNamesSz = cannedKeyAlgoEcc384NamesSz;
|
|
}
|
|
break;
|
|
case ECC_SECP521R1:
|
|
if (ssh->ctx->useCert) {
|
|
#ifdef WOLFSSH_CERTS
|
|
cannedKeyAlgoNames = cannedKeyAlgoX509Ecc521Names;
|
|
cannedKeyAlgoNamesSz =
|
|
cannedKeyAlgoX509Ecc521NamesSz;
|
|
#endif
|
|
}
|
|
else {
|
|
cannedKeyAlgoNames = cannedKeyAlgoEcc521Names;
|
|
cannedKeyAlgoNamesSz =
|
|
cannedKeyAlgoEcc521NamesSz;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
if (ssh->ctx->useCert) {
|
|
#ifdef WOLFSSH_CERTS
|
|
cannedKeyAlgoNames = cannedKeyAlgoX509RsaNames;
|
|
cannedKeyAlgoNamesSz = cannedKeyAlgoX509RsaNamesSz;
|
|
#endif
|
|
}
|
|
else {
|
|
cannedKeyAlgoNames = cannedKeyAlgoRsaNames;
|
|
cannedKeyAlgoNamesSz = cannedKeyAlgoRsaNamesSz;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
cannedKeyAlgoNames = cannedKeyAlgoClientNames;
|
|
cannedKeyAlgoNamesSz = cannedKeyAlgoClientNamesSz;
|
|
}
|
|
payloadSz = MSG_ID_SZ + COOKIE_SZ + (LENGTH_SZ * 11) + BOOLEAN_SZ +
|
|
cannedKexAlgoNamesSz + cannedKeyAlgoNamesSz +
|
|
(cannedEncAlgoNamesSz * 2) +
|
|
(cannedMacAlgoNamesSz * 2) +
|
|
(cannedNoneNamesSz * 2);
|
|
ret = PreparePacket(ssh, payloadSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
payload = output + idx;
|
|
|
|
output[idx++] = MSGID_KEXINIT;
|
|
|
|
ret = wc_RNG_GenerateBlock(ssh->rng, output + idx, COOKIE_SZ);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
byte* buf;
|
|
word32 bufSz = payloadSz + LENGTH_SZ;
|
|
|
|
idx += COOKIE_SZ;
|
|
|
|
CopyNameList(output, &idx, cannedKexAlgoNames, cannedKexAlgoNamesSz);
|
|
CopyNameList(output, &idx, cannedKeyAlgoNames, cannedKeyAlgoNamesSz);
|
|
CopyNameList(output, &idx, cannedEncAlgoNames, cannedEncAlgoNamesSz);
|
|
CopyNameList(output, &idx, cannedEncAlgoNames, cannedEncAlgoNamesSz);
|
|
CopyNameList(output, &idx, cannedMacAlgoNames, cannedMacAlgoNamesSz);
|
|
CopyNameList(output, &idx, cannedMacAlgoNames, cannedMacAlgoNamesSz);
|
|
CopyNameList(output, &idx, cannedNoneNames, cannedNoneNamesSz);
|
|
CopyNameList(output, &idx, cannedNoneNames, cannedNoneNamesSz);
|
|
c32toa(0, output + idx); /* Languages - Client To Server (0) */
|
|
idx += LENGTH_SZ;
|
|
c32toa(0, output + idx); /* Languages - Server To Client (0) */
|
|
idx += LENGTH_SZ;
|
|
output[idx++] = 0; /* First KEX packet follows (false) */
|
|
c32toa(0, output + idx); /* Reserved (0) */
|
|
idx += LENGTH_SZ;
|
|
|
|
if (ssh->handshake->kexInit != NULL) {
|
|
WFREE(ssh->handshake->kexInit, ssh->ctx->heap, DYNTYPE_STRING);
|
|
ssh->handshake->kexInit = NULL;
|
|
ssh->handshake->kexInitSz = 0;
|
|
}
|
|
|
|
buf = (byte*)WMALLOC(bufSz, ssh->ctx->heap, DYNTYPE_STRING);
|
|
if (buf == NULL) {
|
|
WLOG(WS_LOG_DEBUG, "Cannot allocate storage for KEX Init msg");
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
else {
|
|
c32toa(payloadSz, buf);
|
|
WMEMCPY(buf + LENGTH_SZ, payload, payloadSz);
|
|
ssh->handshake->kexInit = buf;
|
|
ssh->handshake->kexInitSz = bufSz;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
/* increase amount to be sent only if BundlePacket will be called */
|
|
ssh->outputBuffer.length = idx;
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
if (ret != WS_WANT_WRITE && ret != WS_SUCCESS)
|
|
PurgePacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendKexInit(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
struct wolfSSH_sigKeyBlockFull {
|
|
byte pubKeyId;
|
|
byte sigId;
|
|
word32 sz;
|
|
const char *name;
|
|
word32 nameSz;
|
|
union {
|
|
#ifndef WOLFSSH_NO_SSH_RSA_SHA1
|
|
struct {
|
|
RsaKey key;
|
|
byte e[257];
|
|
word32 eSz;
|
|
byte ePad;
|
|
byte n[257];
|
|
word32 nSz;
|
|
byte nPad;
|
|
} rsa;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
struct {
|
|
ecc_key key;
|
|
word32 keyBlobSz;
|
|
const char *keyBlobName;
|
|
word32 keyBlobNameSz;
|
|
byte q[257];
|
|
word32 qSz;
|
|
byte qPad;
|
|
const char *primeName;
|
|
word32 primeNameSz;
|
|
} ecc;
|
|
#endif
|
|
} sk;
|
|
};
|
|
|
|
#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256
|
|
/* Size of SABER Level1 ciphertext. */
|
|
#define KEX_F_SIZE 736
|
|
#else
|
|
#define KEX_F_SIZE (256 + 1)
|
|
#endif
|
|
|
|
#define KEX_SIG_SIZE (512)
|
|
|
|
|
|
/* SendKexDhReply()
|
|
* It is also the funciton used for MSGID_KEXECDH_REPLY. The parameters
|
|
* are analogous between the two messages. Where MSGID_KEXDH_REPLY has
|
|
* server's public host key (K_S), f, and the signature of H;
|
|
* MSGID_KEXECDH_REPLY has K_S, the server'e ephemeral public key (Q_S),
|
|
* and the signature of H. This also applies to the GEX version of this.
|
|
* H is calculated the same for KEXDH and KEXECDH, and has some exceptions
|
|
* for GEXDH. */
|
|
int SendKexDhReply(WOLFSSH* ssh)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
void *heap = NULL;
|
|
byte *f_ptr = NULL, *sig_ptr = NULL;
|
|
#ifndef WOLFSSH_NO_ECDH
|
|
byte *r_ptr = NULL, *s_ptr = NULL;
|
|
#endif
|
|
byte scratchLen[LENGTH_SZ];
|
|
word32 fSz = KEX_F_SIZE;
|
|
word32 sigSz = KEX_SIG_SIZE;
|
|
byte useEcc = 0;
|
|
#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256
|
|
byte useSaber = 0;
|
|
#endif
|
|
byte fPad = 0;
|
|
byte kPad = 0;
|
|
word32 sigBlockSz = 0;
|
|
word32 payloadSz = 0;
|
|
word32 scratch = 0;
|
|
byte* output;
|
|
word32 idx;
|
|
byte msgId = MSGID_KEXDH_REPLY;
|
|
enum wc_HashType enmhashId = WC_HASH_TYPE_NONE;
|
|
#ifndef WOLFSSH_NO_DH
|
|
byte *y_ptr = NULL;
|
|
const byte* primeGroup = NULL;
|
|
word32 primeGroupSz = 0;
|
|
const byte* generator = NULL;
|
|
word32 generatorSz = 0;
|
|
#endif
|
|
struct wolfSSH_sigKeyBlockFull *sigKeyBlock_ptr = NULL;
|
|
#ifndef WOLFSSH_SMALL_STACK
|
|
byte f_s[KEX_F_SIZE];
|
|
byte sig_s[KEX_SIG_SIZE];
|
|
|
|
f_ptr = f_s;
|
|
sig_ptr = sig_s;
|
|
#endif
|
|
WLOG(WS_LOG_DEBUG, "Entering SendKexDhReply()");
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (ssh == NULL || ssh->ctx == NULL || ssh->handshake == NULL) {
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
heap = ssh->ctx->heap;
|
|
}
|
|
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
f_ptr = (byte*)WMALLOC(KEX_F_SIZE, heap, DYNTYPE_BUFFER);
|
|
sig_ptr = (byte*)WMALLOC(KEX_SIG_SIZE, heap, DYNTYPE_BUFFER);
|
|
if (f_ptr == NULL || sig_ptr == NULL)
|
|
ret = WS_MEMORY_E;
|
|
#endif
|
|
|
|
sigKeyBlock_ptr = (struct wolfSSH_sigKeyBlockFull*)WMALLOC(
|
|
sizeof(struct wolfSSH_sigKeyBlockFull), heap, DYNTYPE_PRIVKEY);
|
|
if (sigKeyBlock_ptr == NULL)
|
|
ret = WS_MEMORY_E;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WMEMSET(sigKeyBlock_ptr, 0, sizeof(struct wolfSSH_sigKeyBlockFull));
|
|
sigKeyBlock_ptr->pubKeyId = ID_NONE;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
sigKeyBlock_ptr->pubKeyId = ssh->handshake->pubKeyId;
|
|
sigKeyBlock_ptr->sigId = ssh->handshake->sigId;
|
|
sigKeyBlock_ptr->name = IdToName(ssh->handshake->sigId);
|
|
sigKeyBlock_ptr->nameSz = (word32)strlen(sigKeyBlock_ptr->name);
|
|
|
|
switch (ssh->handshake->kexId) {
|
|
#ifndef WOLFSSH_NO_DH_GROUP1_SHA1
|
|
case ID_DH_GROUP1_SHA1:
|
|
primeGroup = dhPrimeGroup1;
|
|
primeGroupSz = dhPrimeGroup1Sz;
|
|
generator = dhGenerator;
|
|
generatorSz = dhGeneratorSz;
|
|
break;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_DH_GROUP14_SHA1
|
|
case ID_DH_GROUP14_SHA1:
|
|
primeGroup = dhPrimeGroup14;
|
|
primeGroupSz = dhPrimeGroup14Sz;
|
|
generator = dhGenerator;
|
|
generatorSz = dhGeneratorSz;
|
|
break;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_DH_GEX_SHA256
|
|
case ID_DH_GEX_SHA256:
|
|
primeGroup = dhPrimeGroup14;
|
|
primeGroupSz = dhPrimeGroup14Sz;
|
|
generator = dhGenerator;
|
|
generatorSz = dhGeneratorSz;
|
|
msgId = MSGID_KEXDH_GEX_REPLY;
|
|
break;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256
|
|
case ID_ECDH_SHA2_NISTP256:
|
|
useEcc = 1;
|
|
msgId = MSGID_KEXDH_REPLY;
|
|
break;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP384
|
|
case ID_ECDH_SHA2_NISTP384:
|
|
useEcc = 1;
|
|
msgId = MSGID_KEXDH_REPLY;
|
|
break;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521
|
|
case ID_ECDH_SHA2_NISTP521:
|
|
useEcc = 1;
|
|
msgId = MSGID_KEXDH_REPLY;
|
|
break;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256
|
|
case ID_SABER_LEVEL1_SHA256:
|
|
useSaber = 1; /* Only support level 1 for now. */
|
|
msgId = MSGID_KEXKEM_REPLY;
|
|
break;
|
|
#endif
|
|
default:
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
|
|
enmhashId = (enum wc_HashType)ssh->handshake->hashId;
|
|
}
|
|
|
|
/* At this point, the exchange hash, H, includes items V_C, V_S, I_C,
|
|
* and I_S. Next add K_S, the server's public host key. K_S will
|
|
* either be RSA or ECDSA public key blob. */
|
|
if (ret == WS_SUCCESS) {
|
|
if (sigKeyBlock_ptr->sigId == ID_SSH_RSA) {
|
|
#ifndef WOLFSSH_NO_SSH_RSA_SHA1
|
|
/* Decode the user-configured RSA private key. */
|
|
sigKeyBlock_ptr->sk.rsa.eSz = sizeof(sigKeyBlock_ptr->sk.rsa.e);
|
|
sigKeyBlock_ptr->sk.rsa.nSz = sizeof(sigKeyBlock_ptr->sk.rsa.n);
|
|
ret = wc_InitRsaKey(&sigKeyBlock_ptr->sk.rsa.key, heap);
|
|
if (ret == 0)
|
|
ret = wc_RsaPrivateKeyDecode(ssh->ctx->privateKey, &scratch,
|
|
&sigKeyBlock_ptr->sk.rsa.key,
|
|
(int)ssh->ctx->privateKeySz);
|
|
/* Flatten the public key into mpint values for the hash. */
|
|
if (ret == 0)
|
|
ret = wc_RsaFlattenPublicKey(&sigKeyBlock_ptr->sk.rsa.key,
|
|
sigKeyBlock_ptr->sk.rsa.e,
|
|
&sigKeyBlock_ptr->sk.rsa.eSz,
|
|
sigKeyBlock_ptr->sk.rsa.n,
|
|
&sigKeyBlock_ptr->sk.rsa.nSz);
|
|
if (ret == 0) {
|
|
/* Add a pad byte if the mpint has the MSB set. */
|
|
ret = CreateMpint(sigKeyBlock_ptr->sk.rsa.e,
|
|
&sigKeyBlock_ptr->sk.rsa.eSz,
|
|
&sigKeyBlock_ptr->sk.rsa.ePad);
|
|
}
|
|
if (ret == 0) {
|
|
/* Add a pad byte if the mpint has the MSB set. */
|
|
ret = CreateMpint(sigKeyBlock_ptr->sk.rsa.n,
|
|
&sigKeyBlock_ptr->sk.rsa.nSz,
|
|
&sigKeyBlock_ptr->sk.rsa.nPad);
|
|
}
|
|
if (ret == 0) {
|
|
sigKeyBlock_ptr->sz = (LENGTH_SZ * 3) + sigKeyBlock_ptr->nameSz +
|
|
sigKeyBlock_ptr->sk.rsa.eSz +
|
|
sigKeyBlock_ptr->sk.rsa.ePad +
|
|
sigKeyBlock_ptr->sk.rsa.nSz +
|
|
sigKeyBlock_ptr->sk.rsa.nPad;
|
|
c32toa(sigKeyBlock_ptr->sz, scratchLen);
|
|
/* Hash in the length of the public key block. */
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the length of the key type string. */
|
|
if (ret == 0) {
|
|
c32toa(sigKeyBlock_ptr->nameSz, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the key type string. */
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
(byte*)sigKeyBlock_ptr->name,
|
|
sigKeyBlock_ptr->nameSz);
|
|
/* Hash in the length of the RSA public key E value. */
|
|
if (ret == 0) {
|
|
c32toa(sigKeyBlock_ptr->sk.rsa.eSz + sigKeyBlock_ptr->sk.rsa.ePad,
|
|
scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the pad byte for the RSA public key E value. */
|
|
if (ret == 0) {
|
|
if (sigKeyBlock_ptr->sk.rsa.ePad) {
|
|
scratchLen[0] = 0;
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId, scratchLen, 1);
|
|
}
|
|
}
|
|
/* Hash in the RSA public key E value. */
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
sigKeyBlock_ptr->sk.rsa.e,
|
|
sigKeyBlock_ptr->sk.rsa.eSz);
|
|
/* Hash in the length of the RSA public key N value. */
|
|
if (ret == 0) {
|
|
c32toa(sigKeyBlock_ptr->sk.rsa.nSz + sigKeyBlock_ptr->sk.rsa.nPad,
|
|
scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the pad byte for the RSA public key N value. */
|
|
if (ret == 0) {
|
|
if (sigKeyBlock_ptr->sk.rsa.nPad) {
|
|
scratchLen[0] = 0;
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId, scratchLen, 1);
|
|
}
|
|
}
|
|
/* Hash in the RSA public key N value. */
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
sigKeyBlock_ptr->sk.rsa.n,
|
|
sigKeyBlock_ptr->sk.rsa.nSz);
|
|
#endif /* WOLFSSH_NO_SSH_RSA_SHA1 */
|
|
}
|
|
else if (sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP256 ||
|
|
sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP384 ||
|
|
sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP521) {
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
sigKeyBlock_ptr->sk.ecc.primeName =
|
|
PrimeNameForId(ssh->handshake->sigId);
|
|
sigKeyBlock_ptr->sk.ecc.primeNameSz =
|
|
(word32)strlen(sigKeyBlock_ptr->sk.ecc.primeName);
|
|
|
|
/* Decode the user-configured ECDSA private key. */
|
|
sigKeyBlock_ptr->sk.ecc.qSz = sizeof(sigKeyBlock_ptr->sk.ecc.q);
|
|
ret = wc_ecc_init_ex(&sigKeyBlock_ptr->sk.ecc.key, heap,
|
|
INVALID_DEVID);
|
|
scratch = 0;
|
|
if (ret == 0)
|
|
ret = wc_EccPrivateKeyDecode(ssh->ctx->privateKey, &scratch,
|
|
&sigKeyBlock_ptr->sk.ecc.key,
|
|
ssh->ctx->privateKeySz);
|
|
/* Flatten the public key into x963 value for the exchange hash. */
|
|
if (ret == 0) {
|
|
#ifdef PRIVATE_KEY_UNLOCK
|
|
PRIVATE_KEY_UNLOCK();
|
|
#endif
|
|
ret = wc_ecc_export_x963(&sigKeyBlock_ptr->sk.ecc.key,
|
|
sigKeyBlock_ptr->sk.ecc.q,
|
|
&sigKeyBlock_ptr->sk.ecc.qSz);
|
|
#ifdef PRIVATE_KEY_LOCK
|
|
PRIVATE_KEY_LOCK();
|
|
#endif
|
|
}
|
|
/* Hash in the length of the public key block. */
|
|
if (ret == 0) {
|
|
sigKeyBlock_ptr->sz = (LENGTH_SZ * 3) +
|
|
sigKeyBlock_ptr->nameSz +
|
|
sigKeyBlock_ptr->sk.ecc.primeNameSz +
|
|
sigKeyBlock_ptr->sk.ecc.qSz;
|
|
c32toa(sigKeyBlock_ptr->sz, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the length of the key type string. */
|
|
if (ret == 0) {
|
|
c32toa(sigKeyBlock_ptr->nameSz, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the key type string. */
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
(byte*)sigKeyBlock_ptr->name,
|
|
sigKeyBlock_ptr->nameSz);
|
|
/* Hash in the length of the name of the prime. */
|
|
if (ret == 0) {
|
|
c32toa(sigKeyBlock_ptr->sk.ecc.primeNameSz, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the name of the prime. */
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
(const byte*)sigKeyBlock_ptr->sk.ecc.primeName,
|
|
sigKeyBlock_ptr->sk.ecc.primeNameSz);
|
|
/* Hash in the length of the public key. */
|
|
if (ret == 0) {
|
|
c32toa(sigKeyBlock_ptr->sk.ecc.qSz, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the public key. */
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
sigKeyBlock_ptr->sk.ecc.q,
|
|
sigKeyBlock_ptr->sk.ecc.qSz);
|
|
#endif
|
|
}
|
|
#ifndef WOLFSSH_NO_DH_GEX_SHA256
|
|
/* If using DH-GEX include the GEX specific values. */
|
|
if (ssh->handshake->kexId == ID_DH_GEX_SHA256) {
|
|
byte primeGroupPad = 0, generatorPad = 0;
|
|
|
|
/* Hash in the client's requested minimum key size. */
|
|
if (ret == 0) {
|
|
c32toa(ssh->handshake->dhGexMinSz, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the client's requested preferred key size. */
|
|
if (ret == 0) {
|
|
c32toa(ssh->handshake->dhGexPreferredSz, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the client's requested maximum key size. */
|
|
if (ret == 0) {
|
|
c32toa(ssh->handshake->dhGexMaxSz, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Add a pad byte if the mpint has the MSB set. */
|
|
if (ret == 0) {
|
|
ret = CreateMpint((byte*)primeGroup,
|
|
&primeGroupSz, &primeGroupPad);
|
|
}
|
|
if (ret == 0) {
|
|
/* Hash in the length of the GEX prime group. */
|
|
c32toa(primeGroupSz + primeGroupPad, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the pad byte for the GEX prime group. */
|
|
if (ret == 0) {
|
|
if (primeGroupPad) {
|
|
scratchLen[0] = 0;
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, 1);
|
|
}
|
|
}
|
|
/* Hash in the GEX prime group. */
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
primeGroup, primeGroupSz);
|
|
/* Add a pad byte if the mpint has the MSB set. */
|
|
if (ret == 0) {
|
|
ret = CreateMpint((byte*)generator,
|
|
&generatorSz, &generatorPad);
|
|
}
|
|
if (ret == 0) {
|
|
/* Hash in the length of the GEX generator. */
|
|
c32toa(generatorSz + generatorPad, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the pad byte for the GEX generator. */
|
|
if (ret == 0) {
|
|
if (generatorPad) {
|
|
scratchLen[0] = 0;
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
scratchLen, 1);
|
|
}
|
|
}
|
|
/* Hash in the GEX generator. */
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId,
|
|
generator, generatorSz);
|
|
}
|
|
#endif
|
|
|
|
/* Hash in the size of the client's DH e-value (ECDH Q-value). */
|
|
if (ret == 0) {
|
|
c32toa(ssh->handshake->eSz, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash, enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
/* Hash in the client's DH e-value (ECDH Q-value). */
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash, enmhashId,
|
|
ssh->handshake->e, ssh->handshake->eSz);
|
|
|
|
/* reset size here because a previous shared secret could potentially be
|
|
* smaller by a byte than usual and cause buffer issues with re-key */
|
|
if (ret == 0)
|
|
ssh->kSz = MAX_KEX_KEY_SZ;
|
|
|
|
/* Make the server's DH f-value and the shared secret K. */
|
|
/* Or make the server's ECDH private value, and the shared secret K. */
|
|
if (ret == 0) {
|
|
if (!useEcc
|
|
#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256
|
|
&& !useSaber
|
|
#endif
|
|
) {
|
|
#ifndef WOLFSSH_NO_DH
|
|
word32 ySz = MAX_KEX_KEY_SZ;
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
DhKey *privKey = (DhKey*)WMALLOC(sizeof(DhKey), heap,
|
|
DYNTYPE_PRIVKEY);
|
|
y_ptr = (byte*)WMALLOC(ySz, heap, DYNTYPE_PRIVKEY);
|
|
if (privKey == NULL || y_ptr == NULL)
|
|
ret = WS_MEMORY_E;
|
|
#else
|
|
DhKey privKey[1];
|
|
byte y_s[MAX_KEX_KEY_SZ];
|
|
y_ptr = y_s;
|
|
#endif
|
|
if (ret == WS_SUCCESS) {
|
|
ret = wc_InitDhKey(privKey);
|
|
if (ret == 0)
|
|
ret = wc_DhSetKey(privKey, primeGroup, primeGroupSz,
|
|
generator, generatorSz);
|
|
if (ret == 0)
|
|
ret = wc_DhGenerateKeyPair(privKey, ssh->rng,
|
|
y_ptr, &ySz, f_ptr, &fSz);
|
|
if (ret == 0) {
|
|
#ifdef PRIVATE_KEY_UNLOCK
|
|
PRIVATE_KEY_UNLOCK();
|
|
#endif
|
|
ret = wc_DhAgree(privKey, ssh->k, &ssh->kSz, y_ptr, ySz,
|
|
ssh->handshake->e, ssh->handshake->eSz);
|
|
#ifdef PRIVATE_KEY_LOCK
|
|
PRIVATE_KEY_LOCK();
|
|
#endif
|
|
}
|
|
ForceZero(y_ptr, ySz);
|
|
wc_FreeDhKey(privKey);
|
|
}
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
if (privKey) {
|
|
WFREE(privKey, heap, DYNTYPE_PRIVKEY);
|
|
}
|
|
#endif
|
|
#endif /* ! WOLFSSH_NO_DH */
|
|
}
|
|
else if (useEcc) {
|
|
#if !defined(WOLFSSH_NO_ECDH)
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
ecc_key *pubKey = NULL, *privKey = NULL;
|
|
pubKey = (ecc_key*)WMALLOC(sizeof(ecc_key), heap,
|
|
DYNTYPE_PUBKEY);
|
|
privKey = (ecc_key*)WMALLOC(sizeof(ecc_key), heap,
|
|
DYNTYPE_PRIVKEY);
|
|
if (pubKey == NULL || privKey == NULL) {
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
#else
|
|
ecc_key pubKey[1];
|
|
ecc_key privKey[1];
|
|
#endif
|
|
int primeId;
|
|
|
|
primeId = wcPrimeForId(ssh->handshake->kexId);
|
|
if (primeId == ECC_CURVE_INVALID)
|
|
ret = WS_INVALID_PRIME_CURVE;
|
|
|
|
if (ret == 0)
|
|
ret = wc_ecc_init_ex(pubKey, heap, INVALID_DEVID);
|
|
if (ret == 0)
|
|
ret = wc_ecc_init_ex(privKey, heap, INVALID_DEVID);
|
|
#ifdef HAVE_WC_ECC_SET_RNG
|
|
if (ret == 0)
|
|
ret = wc_ecc_set_rng(privKey, ssh->rng);
|
|
#endif
|
|
|
|
if (ret == 0)
|
|
ret = wc_ecc_import_x963_ex(ssh->handshake->e,
|
|
ssh->handshake->eSz,
|
|
pubKey, primeId);
|
|
|
|
if (ret == 0)
|
|
ret = wc_ecc_make_key_ex(ssh->rng,
|
|
wc_ecc_get_curve_size_from_id(primeId),
|
|
privKey, primeId);
|
|
if (ret == 0) {
|
|
#ifdef PRIVATE_KEY_UNLOCK
|
|
PRIVATE_KEY_UNLOCK();
|
|
#endif
|
|
ret = wc_ecc_export_x963(privKey, f_ptr, &fSz);
|
|
#ifdef PRIVATE_KEY_LOCK
|
|
PRIVATE_KEY_LOCK();
|
|
#endif
|
|
}
|
|
if (ret == 0) {
|
|
#ifdef PRIVATE_KEY_UNLOCK
|
|
PRIVATE_KEY_UNLOCK();
|
|
#endif
|
|
ret = wc_ecc_shared_secret(privKey, pubKey,
|
|
ssh->k, &ssh->kSz);
|
|
#ifdef PRIVATE_KEY_LOCK
|
|
PRIVATE_KEY_LOCK();
|
|
#endif
|
|
}
|
|
wc_ecc_free(privKey);
|
|
wc_ecc_free(pubKey);
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
WFREE(pubKey, heap, DYNTYPE_PUBKEY);
|
|
WFREE(privKey, heap, DYNTYPE_PRIVKEY);
|
|
pubKey = NULL;
|
|
privKey = NULL;
|
|
#endif
|
|
#endif /* !defined(WOLFSSH_NO_ECDH) */
|
|
}
|
|
#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256
|
|
else if (useSaber) {
|
|
/* This is a KEM. In this case, I need to encapsulate the
|
|
* shared secret. */
|
|
OQS_KEM* kem = NULL;
|
|
ret = 0;
|
|
|
|
if (ret == 0) {
|
|
kem = OQS_KEM_new(OQS_KEM_alg_saber_lightsaber);
|
|
if (kem == NULL) {
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
}
|
|
|
|
if (ret == 0) {
|
|
if (ssh->handshake->eSz != kem->length_public_key) {
|
|
ret = WS_PUBKEY_REJECTED_E;
|
|
}
|
|
}
|
|
|
|
if (ret == 0) {
|
|
if (OQS_KEM_encaps(kem, f_ptr, ssh->k, ssh->handshake->e)
|
|
!= OQS_SUCCESS) {
|
|
ret = WS_PUBKEY_REJECTED_E;
|
|
}
|
|
}
|
|
|
|
if (ret == 0) {
|
|
ssh->kSz = kem->length_shared_secret;
|
|
fSz = kem->length_ciphertext;
|
|
}
|
|
}
|
|
#endif
|
|
else {
|
|
/* This should never happen */
|
|
ret = WS_ERROR;
|
|
}
|
|
}
|
|
|
|
/* Hash in the server's DH f-value. */
|
|
/* Do not want leading zero's removed for SABER. */
|
|
if (ret == 0
|
|
#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256
|
|
&& !useSaber
|
|
#endif
|
|
) {
|
|
ret = CreateMpint(f_ptr, &fSz, &fPad);
|
|
}
|
|
if (ret == 0) {
|
|
c32toa(fSz + fPad, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash, enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
if (ret == 0) {
|
|
if (fPad) {
|
|
scratchLen[0] = 0;
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId, scratchLen, 1);
|
|
}
|
|
}
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId, f_ptr, fSz);
|
|
|
|
/* Hash in the shared secret K. */
|
|
if (ret == 0) {
|
|
ret = CreateMpint(ssh->k, &ssh->kSz, &kPad);
|
|
}
|
|
if (ret == 0) {
|
|
c32toa(ssh->kSz + kPad, scratchLen);
|
|
ret = wc_HashUpdate(&ssh->handshake->hash, enmhashId,
|
|
scratchLen, LENGTH_SZ);
|
|
}
|
|
if (ret == 0) {
|
|
if (kPad) {
|
|
scratchLen[0] = 0;
|
|
ret = wc_HashUpdate(&ssh->handshake->hash,
|
|
enmhashId, scratchLen, 1);
|
|
}
|
|
}
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&ssh->handshake->hash, enmhashId,
|
|
ssh->k, ssh->kSz);
|
|
|
|
/* Save the exchange hash value H, and session ID. */
|
|
if (ret == 0) {
|
|
ret = wc_HashFinal(&ssh->handshake->hash,
|
|
enmhashId, ssh->h);
|
|
wc_HashFree(&ssh->handshake->hash, enmhashId);
|
|
ssh->handshake->hashId = WC_HASH_TYPE_NONE;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
ssh->hSz = wc_HashGetDigestSize(enmhashId);
|
|
if (ssh->sessionIdSz == 0) {
|
|
WMEMCPY(ssh->sessionId, ssh->h, ssh->hSz);
|
|
ssh->sessionIdSz = ssh->hSz;
|
|
}
|
|
}
|
|
|
|
if (ret != WS_SUCCESS)
|
|
ret = WS_CRYPTO_FAILED;
|
|
}
|
|
|
|
/* Sign h with the server's private key. */
|
|
if (ret == WS_SUCCESS) {
|
|
wc_HashAlg digestHash;
|
|
byte digest[WC_MAX_DIGEST_SIZE];
|
|
enum wc_HashType sigHashId;
|
|
|
|
sigHashId = HashForId(ssh->handshake->pubKeyId);
|
|
|
|
ret = wc_HashInit(&digestHash, sigHashId);
|
|
if (ret == 0)
|
|
ret = wc_HashUpdate(&digestHash, sigHashId, ssh->h, ssh->hSz);
|
|
if (ret == 0)
|
|
ret = wc_HashFinal(&digestHash, sigHashId, digest);
|
|
if (ret != 0)
|
|
ret = WS_CRYPTO_FAILED;
|
|
wc_HashFree(&digestHash, sigHashId);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (sigKeyBlock_ptr->pubKeyId == ID_SSH_RSA) {
|
|
#ifndef WOLFSSH_NO_SSH_RSA_SHA1
|
|
word32 encSigSz;
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
byte *encSig = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, heap,
|
|
DYNTYPE_TEMP);
|
|
if (encSig == NULL) {
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
#else
|
|
byte encSig[MAX_ENCODED_SIG_SZ];
|
|
#endif
|
|
{
|
|
encSigSz = wc_EncodeSignature(encSig, digest,
|
|
wc_HashGetDigestSize(sigHashId),
|
|
wc_HashGetOID(sigHashId));
|
|
if (encSigSz <= 0) {
|
|
WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad Encode Sig");
|
|
ret = WS_CRYPTO_FAILED;
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_INFO, "Signing hash with %s.",
|
|
IdToName(ssh->handshake->pubKeyId));
|
|
sigSz = wc_RsaSSL_Sign(encSig, encSigSz, sig_ptr,
|
|
KEX_SIG_SIZE, &sigKeyBlock_ptr->sk.rsa.key,
|
|
ssh->rng);
|
|
if (sigSz <= 0) {
|
|
WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad RSA Sign");
|
|
ret = WS_RSA_E;
|
|
}
|
|
}
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
WFREE(encSig, heap, DYNTYPE_TEMP);
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
else if (sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP256 ||
|
|
sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP384 ||
|
|
sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP521) {
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
WLOG(WS_LOG_INFO, "Signing hash with %s.",
|
|
IdToName(ssh->handshake->pubKeyId));
|
|
sigSz = KEX_SIG_SIZE;
|
|
ret = wc_ecc_sign_hash(digest, wc_HashGetDigestSize(sigHashId),
|
|
sig_ptr, &sigSz,
|
|
ssh->rng, &sigKeyBlock_ptr->sk.ecc.key);
|
|
if (ret != MP_OKAY) {
|
|
WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad ECDSA Sign");
|
|
ret = WS_ECC_E;
|
|
}
|
|
else {
|
|
word32 rSz = MAX_ECC_BYTES + ECC_MAX_PAD_SZ;
|
|
word32 sSz = MAX_ECC_BYTES + ECC_MAX_PAD_SZ;
|
|
byte rPad;
|
|
byte sPad;
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
r_ptr = (byte*)WMALLOC(rSz, heap, DYNTYPE_BUFFER);
|
|
s_ptr = (byte*)WMALLOC(sSz, heap, DYNTYPE_BUFFER);
|
|
if (r_ptr == NULL || s_ptr == NULL)
|
|
ret = WS_MEMORY_E;
|
|
#else
|
|
byte r_s[MAX_ECC_BYTES + ECC_MAX_PAD_SZ];
|
|
byte s_s[MAX_ECC_BYTES + ECC_MAX_PAD_SZ];
|
|
r_ptr = r_s;
|
|
s_ptr = s_s;
|
|
#endif
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_ecc_sig_to_rs(sig_ptr, sigSz, r_ptr, &rSz, s_ptr, &sSz);
|
|
if (ret == 0) {
|
|
idx = 0;
|
|
rPad = (r_ptr[0] & 0x80) ? 1 : 0;
|
|
sPad = (s_ptr[0] & 0x80) ? 1 : 0;
|
|
sigSz = (LENGTH_SZ * 2) + rSz + rPad + sSz + sPad;
|
|
|
|
c32toa(rSz + rPad, sig_ptr + idx);
|
|
idx += LENGTH_SZ;
|
|
if (rPad)
|
|
sig_ptr[idx++] = 0;
|
|
WMEMCPY(sig_ptr + idx, r_ptr, rSz);
|
|
idx += rSz;
|
|
c32toa(sSz + sPad, sig_ptr + idx);
|
|
idx += LENGTH_SZ;
|
|
if (sPad)
|
|
sig_ptr[idx++] = 0;
|
|
WMEMCPY(sig_ptr + idx, s_ptr, sSz);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sigKeyBlock_ptr != NULL) {
|
|
if (sigKeyBlock_ptr->sigId == ID_SSH_RSA) {
|
|
#ifndef WOLFSSH_NO_SSH_RSA_SHA1
|
|
wc_FreeRsaKey(&sigKeyBlock_ptr->sk.rsa.key);
|
|
#endif
|
|
}
|
|
else if (sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP256 ||
|
|
sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP384 ||
|
|
sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP521) {
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
wc_ecc_free(&sigKeyBlock_ptr->sk.ecc.key);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = GenerateKeys(ssh, enmhashId);
|
|
|
|
/* Get the buffer, copy the packet data, once f is laid into the buffer,
|
|
* add it to the hash and then add K. */
|
|
if (ret == WS_SUCCESS) {
|
|
sigBlockSz = (LENGTH_SZ * 2) + sigKeyBlock_ptr->nameSz + sigSz;
|
|
payloadSz = MSG_ID_SZ + (LENGTH_SZ * 3) +
|
|
sigKeyBlock_ptr->sz + fSz + fPad + sigBlockSz;
|
|
ret = PreparePacket(ssh, payloadSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = msgId;
|
|
|
|
/* Copy the rsaKeyBlock into the buffer. */
|
|
c32toa(sigKeyBlock_ptr->sz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
c32toa(sigKeyBlock_ptr->nameSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, sigKeyBlock_ptr->name, sigKeyBlock_ptr->nameSz);
|
|
idx += sigKeyBlock_ptr->nameSz;
|
|
if (sigKeyBlock_ptr->sigId == ID_SSH_RSA) {
|
|
#ifndef WOLFSSH_NO_SSH_RSA_SHA1
|
|
c32toa(sigKeyBlock_ptr->sk.rsa.eSz + sigKeyBlock_ptr->sk.rsa.ePad,
|
|
output + idx);
|
|
idx += LENGTH_SZ;
|
|
if (sigKeyBlock_ptr->sk.rsa.ePad) output[idx++] = 0;
|
|
WMEMCPY(output + idx, sigKeyBlock_ptr->sk.rsa.e, sigKeyBlock_ptr->sk.rsa.eSz);
|
|
idx += sigKeyBlock_ptr->sk.rsa.eSz;
|
|
c32toa(sigKeyBlock_ptr->sk.rsa.nSz + sigKeyBlock_ptr->sk.rsa.nPad,
|
|
output + idx);
|
|
idx += LENGTH_SZ;
|
|
if (sigKeyBlock_ptr->sk.rsa.nPad) output[idx++] = 0;
|
|
WMEMCPY(output + idx, sigKeyBlock_ptr->sk.rsa.n, sigKeyBlock_ptr->sk.rsa.nSz);
|
|
idx += sigKeyBlock_ptr->sk.rsa.nSz;
|
|
#endif
|
|
}
|
|
else if (sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP256 ||
|
|
sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP384 ||
|
|
sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP521) {
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
c32toa(sigKeyBlock_ptr->sk.ecc.primeNameSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, sigKeyBlock_ptr->sk.ecc.primeName,
|
|
sigKeyBlock_ptr->sk.ecc.primeNameSz);
|
|
idx += sigKeyBlock_ptr->sk.ecc.primeNameSz;
|
|
c32toa(sigKeyBlock_ptr->sk.ecc.qSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, sigKeyBlock_ptr->sk.ecc.q,
|
|
sigKeyBlock_ptr->sk.ecc.qSz);
|
|
idx += sigKeyBlock_ptr->sk.ecc.qSz;
|
|
#endif
|
|
}
|
|
|
|
/* Copy the server's public key. F for DE, or Q_S for ECDH. */
|
|
c32toa(fSz + fPad, output + idx);
|
|
idx += LENGTH_SZ;
|
|
if (fPad) output[idx++] = 0;
|
|
WMEMCPY(output + idx, f_ptr, fSz);
|
|
idx += fSz;
|
|
|
|
/* Copy the signature of the exchange hash. */
|
|
c32toa(sigBlockSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
c32toa(sigKeyBlock_ptr->nameSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, sigKeyBlock_ptr->name, sigKeyBlock_ptr->nameSz);
|
|
idx += sigKeyBlock_ptr->nameSz;
|
|
c32toa(sigSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, sig_ptr, sigSz);
|
|
idx += sigSz;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = SendNewKeys(ssh);
|
|
|
|
if (ret != WS_WANT_WRITE && ret != WS_SUCCESS)
|
|
PurgePacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendKexDhReply(), ret = %d", ret);
|
|
if (sigKeyBlock_ptr)
|
|
WFREE(sigKeyBlock_ptr, heap, DYNTYPE_PRIVKEY);
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
if (f_ptr)
|
|
WFREE(f_ptr, heap, DYNTYPE_BUFFER);
|
|
if (sig_ptr)
|
|
WFREE(sig_ptr, heap, DYNTYPE_BUFFER);
|
|
#ifndef WOLFSSH_NO_DH
|
|
if (y_ptr)
|
|
WFREE(y_ptr, heap, DYNTYPE_PRIVKEY);
|
|
#endif
|
|
if (r_ptr)
|
|
WFREE(r_ptr, heap, DYNTYPE_BUFFER);
|
|
if (s_ptr)
|
|
WFREE(s_ptr, heap, DYNTYPE_BUFFER);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendNewKeys(WOLFSSH* ssh)
|
|
{
|
|
byte* output;
|
|
word32 idx = 0;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendNewKeys()");
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_NEWKEYS;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ssh->blockSz = ssh->handshake->blockSz;
|
|
ssh->encryptId = ssh->handshake->encryptId;
|
|
ssh->macSz = ssh->handshake->macSz;
|
|
ssh->macId = ssh->handshake->macId;
|
|
ssh->aeadMode = ssh->handshake->aeadMode;
|
|
WMEMCPY(&ssh->keys, &ssh->handshake->keys, sizeof(Keys));
|
|
|
|
switch (ssh->encryptId) {
|
|
case ID_NONE:
|
|
WLOG(WS_LOG_DEBUG, "SNK: using cipher none");
|
|
break;
|
|
|
|
#ifndef WOLFSSH_NO_AES_CBC
|
|
case ID_AES128_CBC:
|
|
case ID_AES192_CBC:
|
|
case ID_AES256_CBC:
|
|
WLOG(WS_LOG_DEBUG, "SNK: using cipher aes-cbc");
|
|
ret = wc_AesSetKey(&ssh->encryptCipher.aes,
|
|
ssh->keys.encKey, ssh->keys.encKeySz,
|
|
ssh->keys.iv, AES_ENCRYPTION);
|
|
break;
|
|
#endif
|
|
|
|
#ifndef WOLFSSH_NO_AES_CTR
|
|
case ID_AES128_CTR:
|
|
case ID_AES192_CTR:
|
|
case ID_AES256_CTR:
|
|
WLOG(WS_LOG_DEBUG, "SNK: using cipher aes-ctr");
|
|
ret = wc_AesSetKey(&ssh->encryptCipher.aes,
|
|
ssh->keys.encKey, ssh->keys.encKeySz,
|
|
ssh->keys.iv, AES_ENCRYPTION);
|
|
break;
|
|
#endif
|
|
|
|
#ifndef WOLFSSH_NO_AES_GCM
|
|
case ID_AES128_GCM:
|
|
case ID_AES192_GCM:
|
|
case ID_AES256_GCM:
|
|
WLOG(WS_LOG_DEBUG, "SNK: using cipher aes-gcm");
|
|
ret = wc_AesGcmSetKey(&ssh->encryptCipher.aes,
|
|
ssh->keys.encKey, ssh->keys.encKeySz);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
WLOG(WS_LOG_DEBUG, "SNK: using cipher invalid");
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ssh->txCount = 0;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendNewKeys(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifndef WOLFSSH_NO_DH_GEX_SHA256
|
|
int SendKexDhGexRequest(WOLFSSH* ssh)
|
|
{
|
|
byte* output;
|
|
word32 idx = 0;
|
|
word32 payloadSz;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendKexDhGexRequest()");
|
|
if (ssh == NULL || ssh->handshake == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
payloadSz = MSG_ID_SZ + (UINT32_SZ * 3);
|
|
ret = PreparePacket(ssh, payloadSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_KEXDH_GEX_REQUEST;
|
|
|
|
WLOG(WS_LOG_INFO, " min = %u, preferred = %u, max = %u",
|
|
ssh->handshake->dhGexMinSz,
|
|
ssh->handshake->dhGexPreferredSz,
|
|
ssh->handshake->dhGexMaxSz);
|
|
c32toa(ssh->handshake->dhGexMinSz, output + idx);
|
|
idx += UINT32_SZ;
|
|
c32toa(ssh->handshake->dhGexPreferredSz, output + idx);
|
|
idx += UINT32_SZ;
|
|
c32toa(ssh->handshake->dhGexMaxSz, output + idx);
|
|
idx += UINT32_SZ;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendKexDhGexRequest(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendKexDhGexGroup(WOLFSSH* ssh)
|
|
{
|
|
byte* output;
|
|
word32 idx = 0;
|
|
word32 payloadSz;
|
|
const byte* primeGroup = dhPrimeGroup14;
|
|
word32 primeGroupSz = dhPrimeGroup14Sz;
|
|
const byte* generator = dhGenerator;
|
|
word32 generatorSz = dhGeneratorSz;
|
|
byte primePad = 0;
|
|
byte generatorPad = 0;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendKexDhGexGroup()");
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (primeGroup[0] & 0x80)
|
|
primePad = 1;
|
|
|
|
if (generator[0] & 0x80)
|
|
generatorPad = 1;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
payloadSz = MSG_ID_SZ + (LENGTH_SZ * 2) +
|
|
primeGroupSz + primePad +
|
|
generatorSz + generatorPad;
|
|
ret = PreparePacket(ssh, payloadSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_KEXDH_GEX_GROUP;
|
|
|
|
c32toa(primeGroupSz + primePad, output + idx);
|
|
idx += LENGTH_SZ;
|
|
|
|
if (primePad) {
|
|
output[idx] = 0;
|
|
idx += 1;
|
|
}
|
|
|
|
WMEMCPY(output + idx, primeGroup, primeGroupSz);
|
|
idx += primeGroupSz;
|
|
|
|
c32toa(generatorSz + generatorPad, output + idx);
|
|
idx += LENGTH_SZ;
|
|
|
|
if (generatorPad) {
|
|
output[idx] = 0;
|
|
idx += 1;
|
|
}
|
|
|
|
WMEMCPY(output + idx, generator, generatorSz);
|
|
idx += generatorSz;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendKexDhGexGroup(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
|
|
int SendKexDhInit(WOLFSSH* ssh)
|
|
{
|
|
byte* output;
|
|
word32 idx = 0;
|
|
word32 payloadSz;
|
|
#ifndef WOLFSSH_NO_DH
|
|
const byte* primeGroup = NULL;
|
|
word32 primeGroupSz = 0;
|
|
const byte* generator = NULL;
|
|
word32 generatorSz = 0;
|
|
#endif
|
|
int ret = WS_SUCCESS;
|
|
byte msgId = MSGID_KEXDH_INIT;
|
|
byte e[MAX_KEX_KEY_SZ+1]; /* plus 1 in case of padding. */
|
|
word32 eSz = sizeof(e);
|
|
byte ePad = 0;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendKexDhInit()");
|
|
|
|
switch (ssh->handshake->kexId) {
|
|
#ifndef WOLFSSH_NO_DH_GROUP1_SHA1
|
|
case ID_DH_GROUP1_SHA1:
|
|
primeGroup = dhPrimeGroup1;
|
|
primeGroupSz = dhPrimeGroup1Sz;
|
|
generator = dhGenerator;
|
|
generatorSz = dhGeneratorSz;
|
|
break;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_DH_GROUP14_SHA1
|
|
case ID_DH_GROUP14_SHA1:
|
|
primeGroup = dhPrimeGroup14;
|
|
primeGroupSz = dhPrimeGroup14Sz;
|
|
generator = dhGenerator;
|
|
generatorSz = dhGeneratorSz;
|
|
break;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_DH_GEX_SHA256
|
|
case ID_DH_GEX_SHA256:
|
|
primeGroup = ssh->handshake->primeGroup;
|
|
primeGroupSz = ssh->handshake->primeGroupSz;
|
|
generator = ssh->handshake->generator;
|
|
generatorSz = ssh->handshake->generatorSz;
|
|
msgId = MSGID_KEXDH_GEX_INIT;
|
|
break;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256
|
|
case ID_ECDH_SHA2_NISTP256:
|
|
ssh->handshake->useEcc = 1;
|
|
msgId = MSGID_KEXECDH_INIT;
|
|
break;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP384
|
|
case ID_ECDH_SHA2_NISTP384:
|
|
ssh->handshake->useEcc = 1;
|
|
msgId = MSGID_KEXECDH_INIT;
|
|
break;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521
|
|
case ID_ECDH_SHA2_NISTP521:
|
|
ssh->handshake->useEcc = 1;
|
|
msgId = MSGID_KEXECDH_INIT;
|
|
break;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256
|
|
case ID_SABER_LEVEL1_SHA256:
|
|
/* Only support level 1 for now. */
|
|
ssh->handshake->useSaber = 1;
|
|
msgId = MSGID_KEXKEM_INIT;
|
|
break;
|
|
#endif
|
|
default:
|
|
WLOG(WS_LOG_DEBUG, "Invalid algo: %u", ssh->handshake->kexId);
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (!ssh->handshake->useEcc
|
|
#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256
|
|
&& !ssh->handshake->useSaber
|
|
#endif
|
|
) {
|
|
#ifndef WOLFSSH_NO_DH
|
|
DhKey* privKey = &ssh->handshake->privKey.dh;
|
|
|
|
ret = wc_InitDhKey(privKey);
|
|
if (ret == 0)
|
|
ret = wc_DhSetKey(privKey, primeGroup, primeGroupSz,
|
|
generator, generatorSz);
|
|
if (ret == 0)
|
|
ret = wc_DhGenerateKeyPair(privKey, ssh->rng,
|
|
ssh->handshake->x,
|
|
&ssh->handshake->xSz,
|
|
e, &eSz);
|
|
#endif
|
|
}
|
|
else if (ssh->handshake->useEcc) {
|
|
#if !defined(WOLFSSH_NO_ECDH)
|
|
ecc_key* privKey = &ssh->handshake->privKey.ecc;
|
|
int primeId = wcPrimeForId(ssh->handshake->kexId);
|
|
|
|
if (primeId == ECC_CURVE_INVALID)
|
|
ret = WS_INVALID_PRIME_CURVE;
|
|
|
|
if (ret == 0)
|
|
ret = wc_ecc_init_ex(privKey, ssh->ctx->heap,
|
|
INVALID_DEVID);
|
|
#ifdef HAVE_WC_ECC_SET_RNG
|
|
if (ret == 0)
|
|
ret = wc_ecc_set_rng(privKey, ssh->rng);
|
|
#endif
|
|
if (ret == 0)
|
|
ret = wc_ecc_make_key_ex(ssh->rng,
|
|
wc_ecc_get_curve_size_from_id(primeId),
|
|
privKey, primeId);
|
|
if (ret == 0) {
|
|
#ifdef PRIVATE_KEY_UNLOCK
|
|
PRIVATE_KEY_UNLOCK();
|
|
#endif
|
|
ret = wc_ecc_export_x963(privKey, e, &eSz);
|
|
#ifdef PRIVATE_KEY_LOCK
|
|
PRIVATE_KEY_LOCK();
|
|
#endif
|
|
}
|
|
#else
|
|
ret = WS_INVALID_ALGO_ID;
|
|
#endif /* !defined(WOLFSSH_NO_ECDH) */
|
|
}
|
|
#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256
|
|
else if (ssh->handshake->useSaber) {
|
|
OQS_KEM* kem = NULL;
|
|
ret = 0;
|
|
|
|
kem = OQS_KEM_new(OQS_KEM_alg_saber_lightsaber);
|
|
if (kem == NULL) {
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
if (OQS_KEM_keypair(kem, e, ssh->handshake->x)
|
|
!= OQS_SUCCESS) {
|
|
/* This should never happen */
|
|
ret = WS_ERROR;
|
|
}
|
|
eSz = kem->length_public_key;
|
|
ssh->handshake->xSz = kem->length_secret_key;
|
|
}
|
|
}
|
|
#endif
|
|
else {
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
|
|
if (ret == 0)
|
|
ret = WS_SUCCESS;
|
|
}
|
|
|
|
/* Do not want leading zero's removed for SABER. */
|
|
if (ret == WS_SUCCESS
|
|
#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256
|
|
&& !ssh->handshake->useSaber
|
|
#endif
|
|
) {
|
|
ret = CreateMpint(e, &eSz, &ePad);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (ePad == 1) {
|
|
ssh->handshake->e[0] = 0;
|
|
}
|
|
WMEMCPY(ssh->handshake->e + ePad, e, eSz);
|
|
ssh->handshake->eSz = eSz + ePad;
|
|
|
|
payloadSz = MSG_ID_SZ + LENGTH_SZ + eSz + ePad;
|
|
ret = PreparePacket(ssh, payloadSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = msgId;
|
|
|
|
c32toa(eSz + ePad, output + idx);
|
|
idx += LENGTH_SZ;
|
|
|
|
if (ePad) {
|
|
output[idx] = 0;
|
|
idx++;
|
|
}
|
|
|
|
WMEMCPY(output + idx, e, eSz);
|
|
idx += eSz;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendKexDhInit(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendUnimplemented(WOLFSSH* ssh)
|
|
{
|
|
byte* output;
|
|
word32 idx = 0;
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL) {
|
|
WLOG(WS_LOG_DEBUG, "Entering SendUnimplemented(), no parameter");
|
|
ret = WS_BAD_ARGUMENT;
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendUnimplemented(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG,
|
|
"Entering SendUnimplemented(), peerSeq = %u", ssh->peerSeq);
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + LENGTH_SZ);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_UNIMPLEMENTED;
|
|
c32toa(ssh->peerSeq, output + idx);
|
|
idx += UINT32_SZ;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendUnimplemented(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendDisconnect(WOLFSSH* ssh, word32 reason)
|
|
{
|
|
byte* output;
|
|
word32 idx = 0;
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ + (LENGTH_SZ * 2));
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_DISCONNECT;
|
|
c32toa(reason, output + idx);
|
|
idx += UINT32_SZ;
|
|
c32toa(0, output + idx);
|
|
idx += LENGTH_SZ;
|
|
c32toa(0, output + idx);
|
|
idx += LENGTH_SZ;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendIgnore(WOLFSSH* ssh, const unsigned char* data, word32 dataSz)
|
|
{
|
|
byte* output;
|
|
word32 idx = 0;
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL || (data == NULL && dataSz > 0))
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + LENGTH_SZ + dataSz);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_IGNORE;
|
|
c32toa(dataSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
if (dataSz > 0) {
|
|
WMEMCPY(output + idx, data, dataSz);
|
|
idx += dataSz;
|
|
}
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int SendGlobalRequest(WOLFSSH* ssh, const unsigned char* data, word32 dataSz, int reply)
|
|
{
|
|
byte* output;
|
|
word32 idx = 0;
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL || (data == NULL && dataSz > 0))
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Enter SendGlobalRequest");
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + LENGTH_SZ + dataSz + BOOLEAN_SZ);
|
|
WLOG(WS_LOG_DEBUG, "Done PreparePacket");
|
|
|
|
if (ret == WS_SUCCESS)
|
|
{
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_GLOBAL_REQUEST;
|
|
c32toa(dataSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
if (dataSz > 0)
|
|
{
|
|
WMEMCPY(output + idx, data, dataSz);
|
|
idx += dataSz;
|
|
}
|
|
|
|
output[idx++] = reply;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
WLOG(WS_LOG_DEBUG, "Done BundlePacket");
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendServiceRequest(), ret = %d", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const char cannedLangTag[] = "en-us";
|
|
static const word32 cannedLangTagSz = sizeof(cannedLangTag) - 1;
|
|
|
|
|
|
int SendDebug(WOLFSSH* ssh, byte alwaysDisplay, const char* msg)
|
|
{
|
|
word32 msgSz;
|
|
byte* output;
|
|
word32 idx = 0;
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
msgSz = (msg != NULL) ? (word32)WSTRLEN(msg) : 0;
|
|
|
|
ret = PreparePacket(ssh,
|
|
MSG_ID_SZ + BOOLEAN_SZ + (LENGTH_SZ * 2) +
|
|
msgSz + cannedLangTagSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_DEBUG;
|
|
output[idx++] = (alwaysDisplay != 0);
|
|
c32toa(msgSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
if (msgSz > 0) {
|
|
WMEMCPY(output + idx, msg, msgSz);
|
|
idx += msgSz;
|
|
}
|
|
c32toa(cannedLangTagSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, cannedLangTag, cannedLangTagSz);
|
|
idx += cannedLangTagSz;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendServiceRequest(WOLFSSH* ssh, byte serviceId)
|
|
{
|
|
const char* serviceName;
|
|
word32 serviceNameSz;
|
|
byte* output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendServiceRequest()");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
serviceName = IdToName(serviceId);
|
|
serviceNameSz = (word32)WSTRLEN(serviceName);
|
|
|
|
ret = PreparePacket(ssh,
|
|
MSG_ID_SZ + LENGTH_SZ + serviceNameSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_SERVICE_REQUEST;
|
|
c32toa(serviceNameSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, serviceName, serviceNameSz);
|
|
idx += serviceNameSz;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendServiceRequest(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendServiceAccept(WOLFSSH* ssh, byte serviceId)
|
|
{
|
|
const char* serviceName;
|
|
word32 serviceNameSz;
|
|
byte* output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
serviceName = IdToName(serviceId);
|
|
serviceNameSz = (word32)WSTRLEN(serviceName);
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + LENGTH_SZ + serviceNameSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_SERVICE_ACCEPT;
|
|
c32toa(serviceNameSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, serviceName, serviceNameSz);
|
|
idx += serviceNameSz;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = SendUserAuthBanner(ssh);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
typedef struct WS_KeySignature {
|
|
byte keySigId;
|
|
word32 sigSz;
|
|
const char *name;
|
|
word32 nameSz;
|
|
union {
|
|
#ifndef WOLFSSH_NO_RSA
|
|
struct {
|
|
RsaKey key;
|
|
byte e[256];
|
|
word32 eSz;
|
|
byte ePad;
|
|
byte n[256];
|
|
word32 nSz;
|
|
byte nPad;
|
|
} rsa;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
struct {
|
|
ecc_key key;
|
|
word32 keyBlobSz;
|
|
const char *keyBlobName;
|
|
word32 keyBlobNameSz;
|
|
byte q[256];
|
|
word32 qSz;
|
|
byte qPad;
|
|
const char *primeName;
|
|
word32 primeNameSz;
|
|
} ecc;
|
|
#endif
|
|
} ks;
|
|
} WS_KeySignature;
|
|
|
|
|
|
static const char cannedAuths[] =
|
|
#if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA)
|
|
"publickey,"
|
|
#endif
|
|
"password,";
|
|
static const word32 cannedAuthsSz = sizeof(cannedAuths) - 2;
|
|
|
|
|
|
/* Updates the payload size, and maybe loads keys. */
|
|
static int PrepareUserAuthRequestPassword(WOLFSSH* ssh, word32* payloadSz,
|
|
const WS_UserAuthData* authData)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL || payloadSz == NULL || authData == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
*payloadSz += BOOLEAN_SZ + LENGTH_SZ +
|
|
authData->sf.password.passwordSz;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int BuildUserAuthRequestPassword(WOLFSSH* ssh,
|
|
byte* output, word32* idx,
|
|
const WS_UserAuthData* authData)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
word32 begin;
|
|
|
|
if (ssh == NULL || output == NULL || idx == NULL || authData == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
output[begin++] = 0; /* Boolean "FALSE" for password change */
|
|
c32toa(authData->sf.password.passwordSz, output + begin);
|
|
begin += LENGTH_SZ;
|
|
WMEMCPY(output + begin, authData->sf.password.password,
|
|
authData->sf.password.passwordSz);
|
|
begin += authData->sf.password.passwordSz;
|
|
*idx = begin;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifndef WOLFSSH_NO_RSA
|
|
static int PrepareUserAuthRequestRsa(WOLFSSH* ssh, word32* payloadSz,
|
|
const WS_UserAuthData* authData, WS_KeySignature* keySig)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL || payloadSz == NULL || authData == NULL || keySig == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_InitRsaKey(&keySig->ks.rsa.key, NULL);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
word32 idx = 0;
|
|
#ifdef WOLFSSH_AGENT
|
|
if (ssh->agentEnabled)
|
|
ret = wc_RsaPublicKeyDecode(authData->sf.publicKey.publicKey,
|
|
&idx, &keySig->ks.rsa.key,
|
|
authData->sf.publicKey.publicKeySz);
|
|
else
|
|
#endif
|
|
ret = wc_RsaPrivateKeyDecode(authData->sf.publicKey.privateKey,
|
|
&idx, &keySig->ks.rsa.key,
|
|
authData->sf.publicKey.privateKeySz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (authData->sf.publicKey.hasSignature) {
|
|
int sigSz = wc_RsaEncryptSize(&keySig->ks.rsa.key);
|
|
|
|
if (sigSz >= 0) {
|
|
*payloadSz += (LENGTH_SZ * 3) + (word32)sigSz +
|
|
authData->sf.publicKey.publicKeyTypeSz;
|
|
keySig->sigSz = sigSz;
|
|
}
|
|
else
|
|
ret = sigSz;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int BuildUserAuthRequestRsa(WOLFSSH* ssh,
|
|
byte* output, word32* idx,
|
|
const WS_UserAuthData* authData,
|
|
const byte* sigStart, word32 sigStartIdx,
|
|
WS_KeySignature* keySig)
|
|
{
|
|
wc_HashAlg hash;
|
|
byte digest[WC_MAX_DIGEST_SIZE];
|
|
word32 digestSz;
|
|
word32 begin;
|
|
enum wc_HashType hashId = WC_HASH_TYPE_SHA;
|
|
int ret = WS_SUCCESS;
|
|
byte* checkData = NULL;
|
|
word32 checkDataSz = 0;
|
|
|
|
if (ssh == NULL || output == NULL || idx == NULL || authData == NULL ||
|
|
sigStart == NULL || keySig == NULL) {
|
|
ret = WS_BAD_ARGUMENT;
|
|
return ret;
|
|
}
|
|
|
|
begin = *idx;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
hashId = HashForId(keySig->keySigId);
|
|
if (hashId == WC_HASH_TYPE_NONE)
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
if (ret == WS_SUCCESS) {
|
|
int checkSz = wc_HashGetDigestSize(hashId);
|
|
if (checkSz > 0)
|
|
digestSz = (word32)checkSz;
|
|
else
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
if (ret == WS_SUCCESS) {
|
|
checkDataSz = LENGTH_SZ + ssh->sessionIdSz + (begin - sigStartIdx);
|
|
checkData = (byte*)WMALLOC(checkDataSz, ssh->ctx->heap, DYNTYPE_TEMP);
|
|
if (checkData == NULL)
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
word32 i = 0;
|
|
|
|
c32toa(ssh->sessionIdSz, checkData + i);
|
|
i += LENGTH_SZ;
|
|
WMEMCPY(checkData + i, ssh->sessionId, ssh->sessionIdSz);
|
|
i += ssh->sessionIdSz;
|
|
WMEMCPY(checkData + i, sigStart, begin - sigStartIdx);
|
|
}
|
|
|
|
#ifdef WOLFSSH_AGENT
|
|
if (ssh->agentEnabled) {
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_AGENT_SignRequest(ssh, checkData, checkDataSz,
|
|
output + begin + LENGTH_SZ, &keySig->sigSz,
|
|
authData->sf.publicKey.publicKey,
|
|
authData->sf.publicKey.publicKeySz, 0);
|
|
if (ret == WS_SUCCESS) {
|
|
c32toa(keySig->sigSz, output + begin);
|
|
begin += LENGTH_SZ + keySig->sigSz;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (ret == WS_SUCCESS) {
|
|
byte encDigest[MAX_ENCODED_SIG_SZ];
|
|
int encDigestSz;
|
|
|
|
WMEMSET(digest, 0, sizeof(digest));
|
|
ret = wc_HashInit(&hash, hashId);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashUpdate(&hash, hashId, checkData, checkDataSz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashFinal(&hash, hashId, digest);
|
|
|
|
c32toa(keySig->sigSz + 7 + LENGTH_SZ * 2, output + begin);
|
|
begin += LENGTH_SZ;
|
|
c32toa(7, output + begin);
|
|
begin += LENGTH_SZ;
|
|
WMEMCPY(output + begin, "ssh-rsa", 7);
|
|
begin += 7;
|
|
c32toa(keySig->sigSz, output + begin);
|
|
begin += LENGTH_SZ;
|
|
encDigestSz = wc_EncodeSignature(encDigest, digest, digestSz,
|
|
wc_HashGetOID(hashId));
|
|
if (encDigestSz <= 0) {
|
|
WLOG(WS_LOG_DEBUG, "SUAR: Bad Encode Sig");
|
|
ret = WS_CRYPTO_FAILED;
|
|
}
|
|
else {
|
|
int sigSz;
|
|
WLOG(WS_LOG_INFO, "Signing hash with RSA.");
|
|
sigSz = wc_RsaSSL_Sign(encDigest, encDigestSz,
|
|
output + begin, keySig->sigSz,
|
|
&keySig->ks.rsa.key, ssh->rng);
|
|
if (sigSz <= 0 || (word32)sigSz != keySig->sigSz) {
|
|
WLOG(WS_LOG_DEBUG, "SUAR: Bad RSA Sign");
|
|
ret = WS_RSA_E;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
begin += keySig->sigSz;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
*idx = begin;
|
|
|
|
if (checkData != NULL) {
|
|
ForceZero(checkData, checkDataSz);
|
|
WFREE(checkData, ssh->ctx->heap, DYNTYPE_TEMP);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
static int PrepareUserAuthRequestEcc(WOLFSSH* ssh, word32* payloadSz,
|
|
const WS_UserAuthData* authData, WS_KeySignature* keySig)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering PrepareUserAuthRequestEcc()");
|
|
if (ssh == NULL || payloadSz == NULL || authData == NULL || keySig == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_ecc_init(&keySig->ks.ecc.key);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
word32 idx = 0;
|
|
#ifdef WOLFSSH_AGENT
|
|
if (ssh->agentEnabled) {
|
|
word32 sz;
|
|
const byte* c = (const byte*)authData->sf.publicKey.publicKey;
|
|
|
|
ato32(c + idx, &sz);
|
|
idx += LENGTH_SZ + sz;
|
|
ato32(c + idx, &sz);
|
|
idx += LENGTH_SZ + sz;
|
|
ato32(c + idx, &sz);
|
|
idx += LENGTH_SZ;
|
|
c += idx;
|
|
idx = 0;
|
|
|
|
ret = wc_ecc_import_x963(c, sz, &keySig->ks.ecc.key);
|
|
}
|
|
else
|
|
#endif
|
|
ret = wc_EccPrivateKeyDecode(authData->sf.publicKey.privateKey,
|
|
&idx, &keySig->ks.ecc.key,
|
|
authData->sf.publicKey.privateKeySz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (authData->sf.publicKey.hasSignature) {
|
|
int sigSz = wc_ecc_sig_size(&keySig->ks.ecc.key);
|
|
|
|
if (sigSz >= 0) {
|
|
*payloadSz += (LENGTH_SZ * 5) + (word32)sigSz +
|
|
authData->sf.publicKey.publicKeyTypeSz;
|
|
keySig->sigSz = sigSz;
|
|
}
|
|
else
|
|
ret = sigSz;
|
|
}
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving PrepareUserAuthRequestEcc(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int BuildUserAuthRequestEcc(WOLFSSH* ssh,
|
|
byte* output, word32* idx,
|
|
const WS_UserAuthData* authData,
|
|
const byte* sigStart, word32 sigStartIdx,
|
|
WS_KeySignature* keySig)
|
|
{
|
|
wc_HashAlg hash;
|
|
byte digest[WC_MAX_DIGEST_SIZE];
|
|
word32 digestSz;
|
|
word32 begin;
|
|
enum wc_HashType hashId = WC_HASH_TYPE_SHA;
|
|
int ret = WS_SUCCESS;
|
|
byte* r_ptr;
|
|
byte* s_ptr;
|
|
byte* sig_ptr;
|
|
word32 rSz = ECC_MAX_SIG_SIZE / 2;
|
|
word32 sSz = ECC_MAX_SIG_SIZE / 2;
|
|
word32 sigSz = ECC_MAX_SIG_SIZE;
|
|
byte* checkData = NULL;
|
|
word32 checkDataSz = 0;
|
|
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
r_ptr = (byte*)WMALLOC(rSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
s_ptr = (byte*)WMALLOC(sSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
sig_ptr = (byte*)WMALLOC(sigSz, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (r_ptr == NULL || s_ptr == NULL || sig_ptr == NULL)
|
|
ret = WS_MEMORY_E;
|
|
#else
|
|
byte r_s[ECC_MAX_SIG_SIZE / 2];
|
|
byte s_s[ECC_MAX_SIG_SIZE / 2];
|
|
byte sig_s[ECC_MAX_SIG_SIZE];
|
|
r_ptr = r_s;
|
|
s_ptr = s_s;
|
|
sig_ptr = sig_s;
|
|
#endif
|
|
|
|
if (ssh == NULL || output == NULL || idx == NULL || authData == NULL ||
|
|
sigStart == NULL || keySig == NULL) {
|
|
ret = WS_BAD_ARGUMENT;
|
|
return ret;
|
|
}
|
|
|
|
begin = *idx;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
hashId = HashForId(keySig->keySigId);
|
|
WMEMSET(digest, 0, sizeof(digest));
|
|
digestSz = wc_HashGetDigestSize(hashId);
|
|
checkDataSz = LENGTH_SZ + ssh->sessionIdSz + (begin - sigStartIdx);
|
|
checkData = (byte*)WMALLOC(checkDataSz, ssh->ctx->heap, DYNTYPE_TEMP);
|
|
if (checkData == NULL)
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
word32 i = 0;
|
|
|
|
c32toa(ssh->sessionIdSz, checkData + i);
|
|
i += LENGTH_SZ;
|
|
WMEMCPY(checkData + i, ssh->sessionId, ssh->sessionIdSz);
|
|
i += ssh->sessionIdSz;
|
|
WMEMCPY(checkData + i, sigStart, begin - sigStartIdx);
|
|
}
|
|
|
|
#ifdef WOLFSSH_AGENT
|
|
if (ssh->agentEnabled) {
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_AGENT_SignRequest(ssh, checkData, checkDataSz,
|
|
sig_ptr, &sigSz,
|
|
authData->sf.publicKey.publicKey,
|
|
authData->sf.publicKey.publicKeySz, 0);
|
|
if (ret == WS_SUCCESS) {
|
|
c32toa(sigSz, output + begin);
|
|
begin += LENGTH_SZ;
|
|
XMEMCPY(output + begin, sig_ptr, sigSz);
|
|
begin += sigSz;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_INFO, "Signing hash with ECDSA.");
|
|
ret = wc_HashInit(&hash, hashId);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashUpdate(&hash, hashId, checkData, checkDataSz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashFinal(&hash, hashId, digest);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_ecc_sign_hash(digest, digestSz, sig_ptr, &sigSz,
|
|
ssh->rng, &keySig->ks.ecc.key);
|
|
if (ret != WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "SUAR: Bad ECC Sign");
|
|
ret = WS_ECC_E;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = wc_ecc_sig_to_rs(sig_ptr, sigSz, r_ptr, &rSz, s_ptr, &sSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
byte rPad;
|
|
byte sPad;
|
|
|
|
/* adds a byte of padding if needed to avoid negative values */
|
|
rPad = (r_ptr[0] & 0x80) ? 1 : 0;
|
|
sPad = (s_ptr[0] & 0x80) ? 1 : 0;
|
|
c32toa(rSz + rPad + sSz + sPad +
|
|
cannedKeyAlgoEcc256NamesSz + LENGTH_SZ * 4,
|
|
output + begin);
|
|
begin += LENGTH_SZ;
|
|
|
|
c32toa(cannedKeyAlgoEcc256NamesSz, output + begin);
|
|
begin += LENGTH_SZ;
|
|
|
|
WMEMCPY(output + begin, cannedKeyAlgoEcc256Names,
|
|
cannedKeyAlgoEcc256NamesSz);
|
|
begin += cannedKeyAlgoEcc256NamesSz;
|
|
|
|
c32toa(rSz + rPad + sSz + sPad + LENGTH_SZ * 2, output + begin);
|
|
begin += LENGTH_SZ;
|
|
|
|
c32toa(rSz + rPad, output + begin);
|
|
begin += LENGTH_SZ;
|
|
|
|
if (rPad)
|
|
output[begin++] = 0;
|
|
|
|
WMEMCPY(output + begin, r_ptr, rSz);
|
|
begin += rSz;
|
|
|
|
c32toa(sSz + sPad, output + begin);
|
|
begin += LENGTH_SZ;
|
|
|
|
if (sPad)
|
|
output[begin++] = 0;
|
|
|
|
WMEMCPY(output + begin, s_ptr, sSz);
|
|
begin += sSz;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
*idx = begin;
|
|
|
|
if (checkData != NULL) {
|
|
ForceZero(checkData, checkDataSz);
|
|
WFREE(checkData, ssh->ctx->heap, DYNTYPE_TEMP);
|
|
}
|
|
|
|
#ifdef WOLFSSH_SMALL_STACK
|
|
if (r_ptr)
|
|
WFREE(r_ptr, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (s_ptr)
|
|
WFREE(s_ptr, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (sig_ptr)
|
|
WFREE(sig_ptr, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifdef WOLFSSH_CERTS
|
|
|
|
static int PrepareUserAuthRequestEccCert(WOLFSSH* ssh, word32* payloadSz,
|
|
const WS_UserAuthData* authData, WS_KeySignature* keySig)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering PrepareUserAuthRequestEccCert()");
|
|
if (ssh == NULL || payloadSz == NULL || authData == NULL || keySig == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_ecc_init(&keySig->ks.ecc.key);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
word32 idx = 0;
|
|
#if 0
|
|
#ifdef WOLFSSH_AGENT
|
|
if (ssh->agentEnabled) {
|
|
word32 sz;
|
|
const byte* c = (const byte*)authData->sf.publicKey.publicKey;
|
|
|
|
ato32(c + idx, &sz);
|
|
idx += LENGTH_SZ + sz;
|
|
ato32(c + idx, &sz);
|
|
idx += LENGTH_SZ + sz;
|
|
ato32(c + idx, &sz);
|
|
idx += LENGTH_SZ;
|
|
c += idx;
|
|
idx = 0;
|
|
|
|
ret = wc_ecc_import_x963(c, sz, &keySig->ks.ecc.key);
|
|
}
|
|
else
|
|
#endif
|
|
#endif
|
|
ret = wc_EccPrivateKeyDecode(authData->sf.publicKey.privateKey,
|
|
&idx, &keySig->ks.ecc.key,
|
|
authData->sf.publicKey.privateKeySz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
*payloadSz += (LENGTH_SZ + authData->sf.publicKey.publicKeyTypeSz) +
|
|
(UINT32_SZ * 2); /* certificate and ocsp counts */
|
|
|
|
if (authData->sf.publicKey.hasSignature) {
|
|
int sigSz = wc_ecc_sig_size(&keySig->ks.ecc.key);
|
|
|
|
if (sigSz >= 0) {
|
|
/* 5 lengths: sig(R), sig(S), sig, sig-type, sig-blob */
|
|
*payloadSz += (LENGTH_SZ * 5) + (word32)sigSz +
|
|
authData->sf.publicKey.publicKeyTypeSz;
|
|
keySig->sigSz = sigSz;
|
|
}
|
|
else
|
|
ret = sigSz;
|
|
}
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving PrepareUserAuthRequestEccCert(), ret = %d",
|
|
ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int BuildUserAuthRequestEccCert(WOLFSSH* ssh,
|
|
byte* output, word32* idx,
|
|
const WS_UserAuthData* authData,
|
|
const byte* sigStart, word32 sigStartIdx,
|
|
WS_KeySignature* keySig)
|
|
{
|
|
wc_HashAlg hash;
|
|
byte digest[WC_MAX_DIGEST_SIZE];
|
|
word32 digestSz;
|
|
word32 begin;
|
|
enum wc_HashType hashId = WC_HASH_TYPE_SHA;
|
|
int ret = WS_SUCCESS;
|
|
byte* r;
|
|
byte* s;
|
|
byte sig[139]; /* wc_ecc_sig_size() for a prime521 key. */
|
|
word32 sigSz = sizeof(sig), rSz, sSz;
|
|
byte* checkData = NULL;
|
|
word32 checkDataSz = 0;
|
|
|
|
if (ssh == NULL || output == NULL || idx == NULL || authData == NULL ||
|
|
sigStart == NULL || keySig == NULL) {
|
|
ret = WS_BAD_ARGUMENT;
|
|
return ret;
|
|
}
|
|
|
|
begin = *idx;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
hashId = HashForId(keySig->keySigId);
|
|
WMEMSET(digest, 0, sizeof(digest));
|
|
digestSz = wc_HashGetDigestSize(hashId);
|
|
checkDataSz = LENGTH_SZ + ssh->sessionIdSz + (begin - sigStartIdx);
|
|
checkData = (byte*)WMALLOC(checkDataSz, ssh->ctx->heap, DYNTYPE_TEMP);
|
|
if (checkData == NULL)
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
word32 i = 0;
|
|
|
|
c32toa(ssh->sessionIdSz, checkData + i);
|
|
i += LENGTH_SZ;
|
|
WMEMCPY(checkData + i, ssh->sessionId, ssh->sessionIdSz);
|
|
i += ssh->sessionIdSz;
|
|
WMEMCPY(checkData + i, sigStart, begin - sigStartIdx);
|
|
}
|
|
|
|
#if 0
|
|
#ifdef WOLFSSH_AGENT
|
|
if (ssh->agentEnabled) {
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_AGENT_SignRequest(ssh, checkData, checkDataSz,
|
|
sig, &sigSz,
|
|
authData->sf.publicKey.publicKey,
|
|
authData->sf.publicKey.publicKeySz, 0);
|
|
if (ret == WS_SUCCESS) {
|
|
c32toa(sigSz, output + begin);
|
|
begin += LENGTH_SZ;
|
|
XMEMCPY(output + begin, sig, sigSz);
|
|
begin += sigSz;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
#endif
|
|
{
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_INFO, "Signing hash with ECDSA.");
|
|
ret = wc_HashInit(&hash, hashId);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashUpdate(&hash, hashId, checkData, checkDataSz);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_HashFinal(&hash, hashId, digest);
|
|
if (ret == WS_SUCCESS)
|
|
ret = wc_ecc_sign_hash(digest, digestSz, sig, &sigSz,
|
|
ssh->rng, &keySig->ks.ecc.key);
|
|
if (ret != WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "SUAR: Bad ECC Sign");
|
|
ret = WS_ECC_E;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
rSz = sSz = sizeof(sig) / 2;
|
|
r = sig;
|
|
s = sig + rSz;
|
|
ret = wc_ecc_sig_to_rs(sig, sigSz, r, &rSz, s, &sSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
byte rPad;
|
|
byte sPad;
|
|
|
|
/* adds a byte of padding if needed to avoid negative values */
|
|
rPad = (r[0] & 0x80) ? 1 : 0;
|
|
sPad = (s[0] & 0x80) ? 1 : 0;
|
|
c32toa(rSz + rPad + sSz + sPad +
|
|
cannedKeyAlgoEcc256NamesSz + LENGTH_SZ * 4,
|
|
output + begin);
|
|
begin += LENGTH_SZ;
|
|
|
|
c32toa(cannedKeyAlgoEcc256NamesSz, output + begin);
|
|
begin += LENGTH_SZ;
|
|
|
|
WMEMCPY(output + begin, cannedKeyAlgoEcc256Names,
|
|
cannedKeyAlgoEcc256NamesSz);
|
|
begin += cannedKeyAlgoEcc256NamesSz;
|
|
|
|
c32toa(rSz + rPad + sSz + sPad + LENGTH_SZ * 2, output + begin);
|
|
begin += LENGTH_SZ;
|
|
|
|
c32toa(rSz + rPad, output + begin);
|
|
begin += LENGTH_SZ;
|
|
|
|
if (rPad)
|
|
output[begin++] = 0;
|
|
|
|
WMEMCPY(output + begin, r, rSz);
|
|
begin += rSz;
|
|
|
|
c32toa(sSz + sPad, output + begin);
|
|
begin += LENGTH_SZ;
|
|
|
|
if (sPad)
|
|
output[begin++] = 0;
|
|
|
|
WMEMCPY(output + begin, s, sSz);
|
|
begin += sSz;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
*idx = begin;
|
|
|
|
if (checkData != NULL) {
|
|
ForceZero(checkData, checkDataSz);
|
|
WFREE(checkData, ssh->ctx->heap, DYNTYPE_TEMP);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif /* WOLFSSH_CERTS */
|
|
|
|
#endif /* WOLFSSH_NO_ECDSA */
|
|
|
|
|
|
#if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA)
|
|
static int PrepareUserAuthRequestPublicKey(WOLFSSH* ssh, word32* payloadSz,
|
|
const WS_UserAuthData* authData, WS_KeySignature* keySig)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL || payloadSz == NULL || authData == NULL || keySig == NULL) {
|
|
ret = WS_BAD_ARGUMENT;
|
|
return ret;
|
|
}
|
|
|
|
keySig->keySigId = NameToId(
|
|
(const char*)authData->sf.publicKey.publicKeyType,
|
|
authData->sf.publicKey.publicKeyTypeSz);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
/* Add the boolean size to the payload, and the lengths of
|
|
* the public key algorithm name, and the public key length.
|
|
* For the X509 types, this accounts for ONLY one certificate.*/
|
|
*payloadSz += BOOLEAN_SZ + (LENGTH_SZ * 2) +
|
|
authData->sf.publicKey.publicKeyTypeSz +
|
|
authData->sf.publicKey.publicKeySz;
|
|
}
|
|
|
|
switch (keySig->keySigId) {
|
|
#ifndef WOLFSSH_NO_RSA
|
|
case ID_SSH_RSA:
|
|
ret = PrepareUserAuthRequestRsa(ssh, payloadSz, authData, keySig);
|
|
break;
|
|
#ifdef WOLFSSH_CERTS
|
|
case ID_X509V3_SSH_RSA:
|
|
ret = PrepareUserAuthRequestRsa(ssh, payloadSz, authData, keySig);
|
|
break;
|
|
#endif
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
case ID_ECDSA_SHA2_NISTP256:
|
|
case ID_ECDSA_SHA2_NISTP384:
|
|
case ID_ECDSA_SHA2_NISTP521:
|
|
ret = PrepareUserAuthRequestEcc(ssh, payloadSz, authData, keySig);
|
|
break;
|
|
#ifdef WOLFSSH_CERTS
|
|
case ID_X509V3_ECDSA_SHA2_NISTP256:
|
|
case ID_X509V3_ECDSA_SHA2_NISTP384:
|
|
case ID_X509V3_ECDSA_SHA2_NISTP521:
|
|
ret = PrepareUserAuthRequestEccCert(ssh,
|
|
payloadSz, authData, keySig);
|
|
break;
|
|
#endif
|
|
#endif
|
|
default:
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int BuildUserAuthRequestPublicKey(WOLFSSH* ssh,
|
|
byte* output, word32* idx,
|
|
const WS_UserAuthData* authData,
|
|
const byte* sigStart, word32 sigStartIdx,
|
|
WS_KeySignature* keySig)
|
|
{
|
|
const WS_UserAuthData_PublicKey* pk;
|
|
word32 begin;
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL || output == NULL || idx == NULL || authData == NULL ||
|
|
sigStart == NULL || keySig == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
begin = *idx;
|
|
pk = &authData->sf.publicKey;
|
|
output[begin++] = pk->hasSignature;
|
|
|
|
if (pk->hasSignature) {
|
|
switch (keySig->keySigId) {
|
|
#ifndef WOLFSSH_NO_RSA
|
|
case ID_SSH_RSA:
|
|
#ifdef WOLFSSH_CERTS
|
|
case ID_X509V3_SSH_RSA:
|
|
#endif
|
|
c32toa(pk->publicKeyTypeSz, output + begin);
|
|
begin += LENGTH_SZ;
|
|
WMEMCPY(output + begin, pk->publicKeyType, pk->publicKeyTypeSz);
|
|
begin += pk->publicKeyTypeSz;
|
|
c32toa(pk->publicKeySz, output + begin);
|
|
begin += LENGTH_SZ;
|
|
WMEMCPY(output + begin, pk->publicKey, pk->publicKeySz);
|
|
begin += pk->publicKeySz;
|
|
ret = BuildUserAuthRequestRsa(ssh, output, &begin,
|
|
authData, sigStart, sigStartIdx, keySig);
|
|
break;
|
|
#endif
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
case ID_ECDSA_SHA2_NISTP256:
|
|
case ID_ECDSA_SHA2_NISTP384:
|
|
case ID_ECDSA_SHA2_NISTP521:
|
|
c32toa(pk->publicKeyTypeSz, output + begin);
|
|
begin += LENGTH_SZ;
|
|
WMEMCPY(output + begin, pk->publicKeyType, pk->publicKeyTypeSz);
|
|
begin += pk->publicKeyTypeSz;
|
|
c32toa(pk->publicKeySz, output + begin);
|
|
begin += LENGTH_SZ;
|
|
WMEMCPY(output + begin, pk->publicKey, pk->publicKeySz);
|
|
begin += pk->publicKeySz;
|
|
ret = BuildUserAuthRequestEcc(ssh, output, &begin,
|
|
authData, sigStart, sigStartIdx, keySig);
|
|
break;
|
|
#ifdef WOLFSSH_CERTS
|
|
case ID_X509V3_ECDSA_SHA2_NISTP256:
|
|
case ID_X509V3_ECDSA_SHA2_NISTP384:
|
|
case ID_X509V3_ECDSA_SHA2_NISTP521:
|
|
c32toa(pk->publicKeyTypeSz, output + begin);
|
|
begin += LENGTH_SZ;
|
|
WMEMCPY(output + begin, pk->publicKeyType, pk->publicKeyTypeSz);
|
|
begin += pk->publicKeyTypeSz;
|
|
c32toa((LENGTH_SZ * 2) + (UINT32_SZ * 2) +
|
|
pk->publicKeyTypeSz + pk->publicKeySz,
|
|
output + begin);
|
|
begin += LENGTH_SZ;
|
|
c32toa(pk->publicKeyTypeSz, output + begin);
|
|
begin += LENGTH_SZ;
|
|
WMEMCPY(output + begin, pk->publicKeyType, pk->publicKeyTypeSz);
|
|
begin += pk->publicKeyTypeSz;
|
|
c32toa(1, output + begin); /* cert count */
|
|
begin += UINT32_SZ;
|
|
c32toa(pk->publicKeySz, output + begin);
|
|
begin += LENGTH_SZ;
|
|
WMEMCPY(output + begin, pk->publicKey, pk->publicKeySz);
|
|
begin += pk->publicKeySz;
|
|
c32toa(0, output + begin); /* ocsp count */
|
|
begin += UINT32_SZ;
|
|
ret = BuildUserAuthRequestEccCert(ssh, output, &begin,
|
|
authData, sigStart, sigStartIdx, keySig);
|
|
break;
|
|
#endif
|
|
#endif
|
|
default:
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
}
|
|
else {
|
|
/* TODO: Is this right? */
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
*idx = begin;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void CleanupUserAuthRequestPublicKey(WS_KeySignature* keySig)
|
|
{
|
|
if (keySig != NULL) {
|
|
if (keySig->keySigId == ID_SSH_RSA) {
|
|
#ifndef WOLFSSH_NO_RSA
|
|
wc_FreeRsaKey(&keySig->ks.rsa.key);
|
|
#endif
|
|
}
|
|
else if (keySig->keySigId == ID_ECDSA_SHA2_NISTP256 ||
|
|
keySig->keySigId == ID_ECDSA_SHA2_NISTP384 ||
|
|
keySig->keySigId == ID_ECDSA_SHA2_NISTP521) {
|
|
#ifndef WOLFSSH_NO_ECDSA
|
|
wc_ecc_free(&keySig->ks.ecc.key);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
int SendUserAuthRequest(WOLFSSH* ssh, byte authId, int addSig)
|
|
{
|
|
byte* output;
|
|
word32 idx;
|
|
const char* authName = NULL;
|
|
word32 authNameSz = 0;
|
|
const char* serviceName = NULL;
|
|
word32 serviceNameSz = 0;
|
|
word32 payloadSz = 0;
|
|
int ret = WS_SUCCESS;
|
|
WS_UserAuthData authData;
|
|
WS_KeySignature *keySig_ptr = NULL;
|
|
|
|
(void)addSig;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendUserAuthRequest()");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
keySig_ptr = (WS_KeySignature*)WMALLOC(sizeof(WS_KeySignature),
|
|
ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
if (!keySig_ptr)
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WMEMSET(keySig_ptr, 0, sizeof(WS_KeySignature));
|
|
keySig_ptr->keySigId = ID_NONE;
|
|
|
|
if (ssh->ctx->userAuthCb != NULL) {
|
|
WLOG(WS_LOG_DEBUG, "SUAR: Calling the userauth callback");
|
|
|
|
WMEMSET(&authData, 0, sizeof(authData));
|
|
authData.type = authId;
|
|
authData.username = (const byte*)ssh->userName;
|
|
authData.usernameSz = ssh->userNameSz;
|
|
|
|
if (authId & WOLFSSH_USERAUTH_PASSWORD) {
|
|
ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PASSWORD,
|
|
&authData, ssh->userAuthCtx);
|
|
if (ret != WOLFSSH_USERAUTH_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "SUAR: Couldn't get password");
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "SUAR: Callback successful password");
|
|
authData.type = authId = ID_USERAUTH_PASSWORD;
|
|
}
|
|
}
|
|
/* fall into public key case if password case was not successful */
|
|
if ((ret == WS_FATAL_ERROR) && (authId & WOLFSSH_USERAUTH_PUBLICKEY)) {
|
|
ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PUBLICKEY,
|
|
&authData, ssh->userAuthCtx);
|
|
if (ret != WOLFSSH_USERAUTH_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "SUAR: Couldn't get key");
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "SUAR: Callback successful public key");
|
|
authData.type = authId = ID_USERAUTH_PUBLICKEY;
|
|
}
|
|
}
|
|
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "SUAR: No user auth callback");
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
serviceName = IdToName(ID_SERVICE_CONNECTION);
|
|
serviceNameSz = (word32)WSTRLEN(serviceName);
|
|
authName = IdToName(authId);
|
|
authNameSz = (word32)WSTRLEN(authName);
|
|
|
|
payloadSz = MSG_ID_SZ + (LENGTH_SZ * 3) +
|
|
ssh->userNameSz + serviceNameSz + authNameSz;
|
|
|
|
if (authId == ID_USERAUTH_PASSWORD)
|
|
ret = PrepareUserAuthRequestPassword(ssh, &payloadSz, &authData);
|
|
else if (authId == ID_USERAUTH_PUBLICKEY && !ssh->userAuthPkDone) {
|
|
authData.sf.publicKey.hasSignature = 1;
|
|
ssh->userAuthPkDone = 1;
|
|
ret = PrepareUserAuthRequestPublicKey(ssh, &payloadSz, &authData,
|
|
keySig_ptr);
|
|
}
|
|
else if (authId != ID_NONE)
|
|
ret = WS_INVALID_ALGO_ID;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, payloadSz);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
byte* sigStart;
|
|
word32 sigStartIdx;
|
|
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
sigStart = output + idx;
|
|
sigStartIdx = idx;
|
|
|
|
output[idx++] = MSGID_USERAUTH_REQUEST;
|
|
c32toa(ssh->userNameSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, ssh->userName, ssh->userNameSz);
|
|
idx += ssh->userNameSz;
|
|
|
|
c32toa(serviceNameSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, serviceName, serviceNameSz);
|
|
idx += serviceNameSz;
|
|
|
|
c32toa(authNameSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, authName, authNameSz);
|
|
idx += authNameSz;
|
|
|
|
if (authId == ID_USERAUTH_PASSWORD) {
|
|
(void)sigStart;
|
|
(void)sigStartIdx;
|
|
|
|
ret = BuildUserAuthRequestPassword(ssh, output, &idx, &authData);
|
|
}
|
|
else if (authId == ID_USERAUTH_PUBLICKEY)
|
|
ret = BuildUserAuthRequestPublicKey(ssh, output, &idx, &authData,
|
|
sigStart, sigStartIdx, keySig_ptr);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ssh->outputBuffer.length = idx;
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
}
|
|
|
|
if (authId == ID_USERAUTH_PUBLICKEY)
|
|
CleanupUserAuthRequestPublicKey(keySig_ptr);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
}
|
|
|
|
if (ret != WS_WANT_WRITE && ret != WS_SUCCESS)
|
|
PurgePacket(ssh);
|
|
|
|
ForceZero(&authData, sizeof(WS_UserAuthData));
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendUserAuthRequest(), ret = %d", ret);
|
|
|
|
if (keySig_ptr)
|
|
WFREE(keySig_ptr, ssh->ctx->heap, DYNTYPE_BUFFER);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendUserAuthFailure(WOLFSSH* ssh, byte partialSuccess)
|
|
{
|
|
byte* output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendUserAuthFailure()");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh,
|
|
MSG_ID_SZ + LENGTH_SZ +
|
|
cannedAuthsSz + BOOLEAN_SZ);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_USERAUTH_FAILURE;
|
|
c32toa(cannedAuthsSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, cannedAuths, cannedAuthsSz);
|
|
idx += cannedAuthsSz;
|
|
output[idx++] = (partialSuccess != 0);
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendUserAuthSuccess(WOLFSSH* ssh)
|
|
{
|
|
byte* output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_USERAUTH_SUCCESS;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendUserAuthPkOk(WOLFSSH* ssh,
|
|
const byte* algoName, word32 algoNameSz,
|
|
const byte* publicKey, word32 publicKeySz)
|
|
{
|
|
byte* output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
|
|
if (ssh == NULL ||
|
|
algoName == NULL || algoNameSz == 0 ||
|
|
publicKey == NULL || publicKeySz == 0) {
|
|
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + (LENGTH_SZ * 2) +
|
|
algoNameSz + publicKeySz);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_USERAUTH_PK_OK;
|
|
c32toa(algoNameSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, algoName, algoNameSz);
|
|
idx += algoNameSz;
|
|
c32toa(publicKeySz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, publicKey, publicKeySz);
|
|
idx += publicKeySz;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendUserAuthBanner(WOLFSSH* ssh)
|
|
{
|
|
byte* output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
const char* banner = NULL;
|
|
word32 bannerSz = 0;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendUserAuthBanner()");
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
banner = ssh->ctx->banner;
|
|
bannerSz = ssh->ctx->bannerSz;
|
|
}
|
|
|
|
if (banner != NULL && bannerSz > 0) {
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + (LENGTH_SZ * 2) +
|
|
bannerSz + cannedLangTagSz);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_USERAUTH_BANNER;
|
|
c32toa(bannerSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
if (bannerSz > 0)
|
|
WMEMCPY(output + idx, banner, bannerSz);
|
|
idx += bannerSz;
|
|
c32toa(cannedLangTagSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, cannedLangTag, cannedLangTagSz);
|
|
idx += cannedLangTagSz;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendUserAuthBanner()");
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendRequestSuccess(WOLFSSH *ssh, int success)
|
|
{
|
|
byte *output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendRequestSuccess(), %s",
|
|
success ? "Success" : "Failure");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ);
|
|
|
|
if (ret == WS_SUCCESS)
|
|
{
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = success ? MSGID_REQUEST_SUCCESS : MSGID_REQUEST_FAILURE;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendRequestSuccess(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendGlobalRequestFwdSuccess(WOLFSSH* ssh, int success, word32 port)
|
|
{
|
|
byte *output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendGlobalRequestFwdSuccess(), %s",
|
|
success ? "Success" : "Failure");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + (success ? UINT32_SZ : 0));
|
|
|
|
if (ret == WS_SUCCESS)
|
|
{
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
if (success) {
|
|
output[idx++] = MSGID_REQUEST_SUCCESS;
|
|
c32toa(port, output + idx);
|
|
idx += UINT32_SZ;
|
|
}
|
|
else {
|
|
output[idx++] = MSGID_REQUEST_FAILURE;
|
|
}
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendGlobalRequestFwdSuccess(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int SendChannelOpen(WOLFSSH* ssh, WOLFSSH_CHANNEL* channel,
|
|
byte* channelData, word32 channelDataSz)
|
|
{
|
|
byte* output;
|
|
const char* channelType = NULL;
|
|
word32 channelTypeSz = 0, idx;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendChannelOpen()");
|
|
|
|
if (ssh == NULL || channel == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
if (channelDataSz > 0 && channelData == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
channelType = IdToName(channel->channelType);
|
|
channelTypeSz = (word32)WSTRLEN(channelType);
|
|
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + LENGTH_SZ + channelTypeSz +
|
|
(UINT32_SZ * 3) + channelDataSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_CHANNEL_OPEN;
|
|
c32toa(channelTypeSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, channelType, channelTypeSz);
|
|
idx += channelTypeSz;
|
|
c32toa(channel->channel, output + idx);
|
|
idx += UINT32_SZ;
|
|
c32toa(channel->windowSz, output + idx);
|
|
idx += UINT32_SZ;
|
|
c32toa(channel->maxPacketSz, output + idx);
|
|
idx += UINT32_SZ;
|
|
if (channelDataSz > 0)
|
|
WMEMCPY(output + idx, channelData, channelDataSz);
|
|
idx += channelDataSz;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelOpen(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendChannelOpenSession(WOLFSSH* ssh, WOLFSSH_CHANNEL* channel)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendChannelOpenSession()");
|
|
|
|
ret = SendChannelOpen(ssh, channel, NULL, 0);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelOpenSession(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifdef WOLFSSH_FWD
|
|
int SendChannelOpenForward(WOLFSSH* ssh, WOLFSSH_CHANNEL* channel)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
byte* forwardData = NULL;
|
|
word32 hostSz, originSz, forwardDataSz, idx;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendChannelOpenForward()");
|
|
|
|
if (ssh == NULL || channel == NULL ||
|
|
channel->host == NULL || channel->origin == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
hostSz = (word32)WSTRLEN(channel->host);
|
|
originSz = (word32)WSTRLEN(channel->origin);
|
|
forwardDataSz = UINT32_SZ * 2 + LENGTH_SZ * 2 + hostSz + originSz;
|
|
forwardData = (byte*)WMALLOC(forwardDataSz,
|
|
ssh->ctx->heap, DYNTYPE_TEMP);
|
|
if (forwardData == NULL)
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
c32toa(hostSz, forwardData);
|
|
idx = LENGTH_SZ;
|
|
WMEMCPY(forwardData + idx, channel->host, hostSz);
|
|
idx += hostSz;
|
|
c32toa(channel->hostPort, forwardData + idx);
|
|
idx += UINT32_SZ;
|
|
c32toa(originSz, forwardData + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(forwardData + idx, channel->origin, originSz);
|
|
idx += originSz;
|
|
c32toa(channel->originPort, forwardData + idx);
|
|
|
|
ret = SendChannelOpen(ssh, channel, forwardData, forwardDataSz);
|
|
}
|
|
|
|
if (forwardData)
|
|
WFREE(forwardData, ssh->ctx->heap, DYNTYPE_TEMP);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelOpenForward(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSH_FWD */
|
|
|
|
|
|
int SendChannelOpenConf(WOLFSSH* ssh, WOLFSSH_CHANNEL* channel)
|
|
{
|
|
byte* output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendChannelOpenConf()");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_INFO, " channelId = %u", channel->channel);
|
|
WLOG(WS_LOG_INFO, " peerChannelId = %u", channel->peerChannel);
|
|
WLOG(WS_LOG_INFO, " peerWindowSz = %u", channel->peerWindowSz);
|
|
WLOG(WS_LOG_INFO, " peerMaxPacketSz = %u", channel->peerMaxPacketSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + (UINT32_SZ * 4));
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_CHANNEL_OPEN_CONF;
|
|
c32toa(channel->peerChannel, output + idx);
|
|
idx += UINT32_SZ;
|
|
c32toa(channel->channel, output + idx);
|
|
idx += UINT32_SZ;
|
|
c32toa(channel->windowSz, output + idx);
|
|
idx += UINT32_SZ;
|
|
c32toa(channel->maxPacketSz, output + idx);
|
|
idx += UINT32_SZ;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelOpenConf(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendChannelEof(WOLFSSH* ssh, word32 peerChannelId)
|
|
{
|
|
byte* output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
WOLFSSH_CHANNEL* channel = NULL;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendChannelEof()");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
channel = ChannelFind(ssh, peerChannelId, WS_CHANNEL_ID_PEER);
|
|
if (channel == NULL)
|
|
ret = WS_INVALID_CHANID;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (channel->eofTxd) {
|
|
WLOG(WS_LOG_DEBUG, "Already sent EOF");
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelEof(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_CHANNEL_EOF;
|
|
c32toa(channel->peerChannel, output + idx);
|
|
idx += UINT32_SZ;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
if (ret == WS_SUCCESS)
|
|
channel->eofTxd = 1;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelEof(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendChannelEow(WOLFSSH* ssh, word32 peerChannelId)
|
|
{
|
|
byte* output;
|
|
word32 idx;
|
|
word32 strSz = sizeof("eow@openssh.com");
|
|
int ret = WS_SUCCESS;
|
|
WOLFSSH_CHANNEL* channel = NULL;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendChannelEow()");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS && !ssh->clientOpenSSH) {
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelEow(), not OpenSSH");
|
|
return ret;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
channel = ChannelFind(ssh, peerChannelId, WS_CHANNEL_ID_PEER);
|
|
if (channel == NULL)
|
|
ret = WS_INVALID_CHANID;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ + LENGTH_SZ + strSz +
|
|
BOOLEAN_SZ);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_CHANNEL_REQUEST;
|
|
c32toa(channel->peerChannel, output + idx);
|
|
idx += UINT32_SZ;
|
|
c32toa(strSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, "eow@openssh.com", strSz);
|
|
idx += strSz;
|
|
output[idx++] = 0; // false
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelEow(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendChannelExit(WOLFSSH* ssh, word32 peerChannelId, int status)
|
|
{
|
|
byte* output;
|
|
word32 idx;
|
|
word32 strSz = sizeof("exit-status");
|
|
int ret = WS_SUCCESS;
|
|
WOLFSSH_CHANNEL* channel = NULL;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendChannelExit(), status = %d", status);
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
channel = ChannelFind(ssh, peerChannelId, WS_CHANNEL_ID_PEER);
|
|
if (channel == NULL)
|
|
ret = WS_INVALID_CHANID;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ + LENGTH_SZ + strSz +
|
|
BOOLEAN_SZ + UINT32_SZ);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_CHANNEL_REQUEST;
|
|
c32toa(channel->peerChannel, output + idx);
|
|
idx += UINT32_SZ;
|
|
c32toa(strSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, "exit-status", strSz);
|
|
idx += strSz;
|
|
output[idx++] = 0; // false
|
|
c32toa(status, output + idx);
|
|
idx += UINT32_SZ;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelExit(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendChannelClose(WOLFSSH* ssh, word32 peerChannelId)
|
|
{
|
|
byte* output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
WOLFSSH_CHANNEL* channel = NULL;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendChannelClose()");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
channel = ChannelFind(ssh, peerChannelId, WS_CHANNEL_ID_PEER);
|
|
if (channel == NULL)
|
|
ret = WS_INVALID_CHANID;
|
|
else if (channel->closeTxd) {
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelClose(), already sent");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_CHANNEL_CLOSE;
|
|
c32toa(channel->peerChannel, output + idx);
|
|
idx += UINT32_SZ;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
channel->closeTxd = 1;
|
|
}
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelClose(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendChannelData(WOLFSSH* ssh, word32 channelId,
|
|
byte* data, word32 dataSz)
|
|
{
|
|
byte* output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
WOLFSSH_CHANNEL* channel = NULL;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendChannelData()");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (ssh->isKeying)
|
|
ret = WS_REKEYING;
|
|
}
|
|
|
|
/* if already having data pending try to flush it first and do not continue
|
|
* to que more on fail */
|
|
if (ret == WS_SUCCESS && ssh->outputBuffer.plainSz > 0) {
|
|
WLOG(WS_LOG_DEBUG, "Flushing out want write data");
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
if (ret != WS_SUCCESS) {
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelData(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (ssh->outputBuffer.length != 0)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF);
|
|
if (channel == NULL) {
|
|
WLOG(WS_LOG_DEBUG, "Invalid channel");
|
|
ret = WS_INVALID_CHANID;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
if (channel->peerWindowSz == 0) {
|
|
WLOG(WS_LOG_DEBUG, "channel window is full");
|
|
ret = WS_WINDOW_FULL;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
word32 bound = min(channel->peerWindowSz, channel->peerMaxPacketSz);
|
|
|
|
if (dataSz > bound) {
|
|
WLOG(WS_LOG_DEBUG,
|
|
"Trying to send %u, client will only accept %u, limiting",
|
|
dataSz, bound);
|
|
dataSz = bound;
|
|
}
|
|
|
|
ret = PreparePacket(ssh,
|
|
MSG_ID_SZ + UINT32_SZ + LENGTH_SZ + dataSz);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_CHANNEL_DATA;
|
|
c32toa(channel->peerChannel, output + idx);
|
|
idx += UINT32_SZ;
|
|
c32toa(dataSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, data, dataSz);
|
|
idx += dataSz;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
WLOG(WS_LOG_INFO, " dataSz = %u", dataSz);
|
|
WLOG(WS_LOG_INFO, " peerWindowSz = %u", channel->peerWindowSz);
|
|
channel->peerWindowSz -= dataSz;
|
|
WLOG(WS_LOG_INFO, " update peerWindowSz = %u", channel->peerWindowSz);
|
|
}
|
|
|
|
/* at this point the data has been loaded into WOLFSSH structure and is
|
|
* considered consumed */
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
if (ret == WS_SUCCESS || ret == WS_WANT_WRITE)
|
|
ret = dataSz;
|
|
|
|
if (ssh && ssh->error == WS_WANT_WRITE)
|
|
ssh->outputBuffer.plainSz = dataSz;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelData(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int SendChannelWindowAdjust(WOLFSSH* ssh, word32 channelId,
|
|
word32 bytesToAdd)
|
|
{
|
|
byte* output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
WOLFSSH_CHANNEL* channel;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendChannelWindowAdjust()");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF);
|
|
if (channel == NULL) {
|
|
WLOG(WS_LOG_DEBUG, "Invalid channel");
|
|
ret = WS_INVALID_CHANID;
|
|
}
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + (UINT32_SZ * 2));
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_CHANNEL_WINDOW_ADJUST;
|
|
c32toa(channel->peerChannel, output + idx);
|
|
idx += UINT32_SZ;
|
|
c32toa(bytesToAdd, output + idx);
|
|
idx += UINT32_SZ;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelWindowAdjust(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static const char cannedShellName[] = "shell";
|
|
static const word32 cannedShellNameSz = sizeof(cannedShellName) - 1;
|
|
|
|
static const char cannedSubName[] = "subsystem";
|
|
static const word32 cannedSubNameSz = sizeof(cannedSubName) - 1;
|
|
|
|
static const char cannedExecName[] = "exec";
|
|
static const word32 cannedExecNameSz = sizeof(cannedExecName) - 1;
|
|
|
|
|
|
/* name : command for exec and name for subsystem channels */
|
|
int SendChannelRequest(WOLFSSH* ssh, byte* name, word32 nameSz)
|
|
{
|
|
byte* output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
WOLFSSH_CHANNEL* channel = NULL;
|
|
const char* cType = NULL;
|
|
word32 typeSz = 0;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendChannelRequest()");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
channel = ChannelFind(ssh,
|
|
ssh->defaultPeerChannelId, WS_CHANNEL_ID_PEER);
|
|
if (channel == NULL)
|
|
ret = WS_INVALID_CHANID;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
switch (ssh->connectChannelId) {
|
|
case WOLFSSH_SESSION_SHELL:
|
|
cType = cannedShellName;
|
|
typeSz = cannedShellNameSz;
|
|
break;
|
|
|
|
case WOLFSSH_SESSION_EXEC:
|
|
cType = cannedExecName;
|
|
typeSz = cannedExecNameSz;
|
|
break;
|
|
|
|
case WOLFSSH_SESSION_SUBSYSTEM:
|
|
cType = cannedSubName;
|
|
typeSz = cannedSubNameSz;
|
|
break;
|
|
|
|
default:
|
|
WLOG(WS_LOG_DEBUG, "Unknown channel type");
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ + LENGTH_SZ +
|
|
typeSz + BOOLEAN_SZ +
|
|
((nameSz > 0)? UINT32_SZ : 0) + nameSz);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_CHANNEL_REQUEST;
|
|
c32toa(channel->peerChannel, output + idx);
|
|
idx += UINT32_SZ;
|
|
c32toa(typeSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, cType, typeSz);
|
|
idx += typeSz;
|
|
output[idx++] = 1;
|
|
|
|
if (nameSz > 0) {
|
|
c32toa(nameSz, output + idx);
|
|
idx += UINT32_SZ;
|
|
WMEMCPY(output + idx, name, nameSz);
|
|
idx += nameSz;
|
|
}
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
WLOG(WS_LOG_INFO, "Sending Channel Request: ");
|
|
WLOG(WS_LOG_INFO, " channelId = %u", channel->peerChannel);
|
|
WLOG(WS_LOG_INFO, " type = %s", cType);
|
|
WLOG(WS_LOG_INFO, " wantReply = %u", 1);
|
|
|
|
#ifdef DEBUG_WOLFSSH
|
|
/* only compile in code for checks on type if in debug mode */
|
|
switch (ssh->connectChannelId) {
|
|
case WOLFSSH_SESSION_EXEC:
|
|
WLOG(WS_LOG_INFO, " command = %s", name);
|
|
break;
|
|
|
|
case WOLFSSH_SESSION_SUBSYSTEM:
|
|
WLOG(WS_LOG_INFO, " subsystem = %s", name);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelRequest(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
#if defined(WOLFSSH_TERM) && !defined(NO_FILESYSTEM)
|
|
|
|
#if !defined(USE_WINDOWS_API) && !defined(MICROCHIP_PIC32) && \
|
|
!defined(NO_TERMIOS)
|
|
/* sets terminal mode in buffer and advances idx */
|
|
static void TTYSet(word32 isSet, int type, byte* out, word32* idx)
|
|
{
|
|
if (isSet) isSet = 1;
|
|
out[*idx] = type; *idx += 1;
|
|
c32toa(isSet, out + *idx); *idx += UINT32_SZ;
|
|
}
|
|
|
|
static void TTYCharSet(char flag, int type, byte* out, word32* idx)
|
|
{
|
|
out[*idx] = type; *idx += 1;
|
|
c32toa(flag, out + *idx); *idx += UINT32_SZ;
|
|
}
|
|
#endif /* !USE_WINDOWS_API && !MICROCHIP_PIC32 && !NO_TERMIOS*/
|
|
|
|
|
|
/* create terminal mode string for pseudo-terminal request
|
|
* returns size of buffer */
|
|
static int CreateMode(WOLFSSH* ssh, byte* mode)
|
|
{
|
|
word32 idx = 0;
|
|
int baud = 38400; /* default speed */
|
|
|
|
#if !defined(USE_WINDOWS_API) && !defined(MICROCHIP_PIC32) && \
|
|
!defined(NO_TERMIOS)
|
|
{
|
|
WOLFSSH_TERMIOS term;
|
|
|
|
if (tcgetattr(STDIN_FILENO, &term) != 0) {
|
|
printf("Couldn't get the original terminal settings.\n");
|
|
return -1;
|
|
}
|
|
|
|
/* set baud rate */
|
|
baud = (int)cfgetospeed(&term);
|
|
|
|
/* char type */
|
|
TTYCharSet(term.c_cc[VINTR], WOLFSSH_VINTR, mode, &idx);
|
|
TTYCharSet(term.c_cc[VQUIT], WOLFSSH_VQUIT, mode, &idx);
|
|
TTYCharSet(term.c_cc[VERASE], WOLFSSH_VERASE, mode, &idx);
|
|
TTYCharSet(term.c_cc[VKILL], WOLFSSH_VKILL, mode, &idx);
|
|
TTYCharSet(term.c_cc[VEOF], WOLFSSH_VEOF, mode, &idx);
|
|
TTYCharSet(term.c_cc[VEOL], WOLFSSH_VEOL, mode, &idx);
|
|
TTYCharSet(term.c_cc[VEOL2], WOLFSSH_VEOL2, mode, &idx);
|
|
TTYCharSet(term.c_cc[VSTART], WOLFSSH_VSTART, mode, &idx);
|
|
TTYCharSet(term.c_cc[VSTOP], WOLFSSH_VSTOP, mode, &idx);
|
|
TTYCharSet(term.c_cc[VSUSP], WOLFSSH_VSUSP, mode, &idx);
|
|
#ifdef VDSUSP
|
|
TTYCharSet(term.c_cc[VDSUSP], WOLFSSH_VDSUSP, mode, &idx);
|
|
#endif
|
|
TTYCharSet(term.c_cc[VREPRINT], WOLFSSH_VREPRINT, mode, &idx);
|
|
TTYCharSet(term.c_cc[VWERASE], WOLFSSH_VWERASE, mode, &idx);
|
|
TTYCharSet(term.c_cc[VLNEXT], WOLFSSH_VLNEXT, mode, &idx);
|
|
#ifdef VFLUSH
|
|
TTYCharSet(term.c_cc[VFLUSH], WOLFSSH_VFLUSH, mode, &idx);
|
|
#endif
|
|
#ifdef VSWTCH
|
|
TTYCharSet(term.c_cc[VSWTCH], WOLFSSH_VSWTCH, mode, &idx);
|
|
#endif
|
|
#ifdef VSTATUS
|
|
TTYCharSet(term.c_cc[VSTATUS], WOLFSSH_VSTATUS, mode, &idx);
|
|
#endif
|
|
TTYCharSet(term.c_cc[VDISCARD], WOLFSSH_VDISCARD, mode, &idx);
|
|
|
|
/* c_iflag for input modes */
|
|
TTYSet((term.c_iflag & IGNPAR), WOLFSSH_IGNPAR, mode, &idx);
|
|
TTYSet((term.c_iflag & PARMRK), WOLFSSH_PARMRK, mode, &idx);
|
|
TTYSet((term.c_iflag & INPCK), WOLFSSH_INPCK, mode, &idx);
|
|
TTYSet((term.c_iflag & ISTRIP), WOLFSSH_ISTRIP, mode, &idx);
|
|
TTYSet((term.c_iflag & INLCR), WOLFSSH_INLCR, mode, &idx);
|
|
TTYSet((term.c_iflag & IGNCR), WOLFSSH_IGNCR, mode, &idx);
|
|
TTYSet((term.c_iflag & ICRNL), WOLFSSH_ICRNL, mode, &idx);
|
|
#ifdef IUCLC
|
|
TTYSet((term.c_iflag & IUCLC), WOLFSSH_IUCLC, mode, &idx);
|
|
#endif
|
|
TTYSet((term.c_iflag & IXON), WOLFSSH_IXON, mode, &idx);
|
|
TTYSet((term.c_iflag & IXANY), WOLFSSH_IXANY, mode, &idx);
|
|
TTYSet((term.c_iflag & IXOFF), WOLFSSH_IXOFF, mode, &idx);
|
|
TTYSet((term.c_iflag & IMAXBEL), WOLFSSH_IMAXBEL, mode, &idx);
|
|
|
|
/* c_lflag */
|
|
TTYSet((term.c_lflag & ISIG), WOLFSSH_ISIG, mode, &idx);
|
|
TTYSet((term.c_lflag & ICANON), WOLFSSH_ICANON, mode, &idx);
|
|
#ifdef XCASE
|
|
TTYSet((term.c_lflag & XCASE), WOLFSSH_XCASE, mode, &idx);
|
|
#endif
|
|
TTYSet((term.c_lflag & ECHO), WOLFSSH_ECHO, mode, &idx);
|
|
TTYSet((term.c_lflag & ECHOE), WOLFSSH_ECHOE, mode, &idx);
|
|
TTYSet((term.c_lflag & ECHOK), WOLFSSH_ECHOK, mode, &idx);
|
|
TTYSet((term.c_lflag & ECHONL), WOLFSSH_ECHONL, mode, &idx);
|
|
TTYSet((term.c_lflag & NOFLSH), WOLFSSH_NOFLSH, mode, &idx);
|
|
TTYSet((term.c_lflag & TOSTOP), WOLFSSH_TOSTOP, mode, &idx);
|
|
TTYSet((term.c_lflag & IEXTEN), WOLFSSH_IEXTEN, mode, &idx);
|
|
TTYSet((term.c_lflag & ECHOCTL), WOLFSSH_ECHOCTL, mode, &idx);
|
|
TTYSet((term.c_lflag & ECHOKE), WOLFSSH_ECHOKE, mode, &idx);
|
|
#ifdef PENDIN
|
|
TTYSet((term.c_lflag & PENDIN), WOLFSSH_PENDIN, mode, &idx);
|
|
#endif
|
|
|
|
/* c_oflag */
|
|
TTYSet((term.c_oflag & OPOST), WOLFSSH_OPOST, mode, &idx);
|
|
#ifdef OLCUC
|
|
TTYSet((term.c_oflag & OLCUC), WOLFSSH_OLCUC, mode, &idx);
|
|
#endif
|
|
TTYSet((term.c_oflag & ONLCR), WOLFSSH_ONLCR, mode, &idx);
|
|
TTYSet((term.c_oflag & OCRNL), WOLFSSH_OCRNL, mode, &idx);
|
|
TTYSet((term.c_oflag & ONOCR), WOLFSSH_ONOCR, mode, &idx);
|
|
TTYSet((term.c_oflag & ONLRET), WOLFSSH_ONLRET, mode, &idx);
|
|
|
|
/* c_cflag */
|
|
TTYSet((term.c_cflag & CS7), WOLFSSH_CS7, mode, &idx);
|
|
TTYSet((term.c_cflag & CS8), WOLFSSH_CS8, mode, &idx);
|
|
TTYSet((term.c_cflag & PARENB), WOLFSSH_PARENB, mode, &idx);
|
|
TTYSet((term.c_cflag & PARODD), WOLFSSH_PARODD, mode, &idx);
|
|
}
|
|
#endif /* !USE_WINDOWS_API && !MICROCHIP_PIC32 && !NO_TERMIOS */
|
|
|
|
mode[idx++] = WOLFSSH_TTY_OP_OSPEED;
|
|
c32toa(baud, mode + idx); idx += UINT32_SZ;
|
|
mode[idx++] = WOLFSSH_TTY_OP_ISPEED;
|
|
c32toa(baud, mode + idx); idx += UINT32_SZ;
|
|
|
|
(void)ssh;
|
|
mode[idx++] = WOLFSSH_TTY_OP_END;
|
|
return idx;
|
|
}
|
|
|
|
|
|
/* sends request for pseudo-terminal (rfc 4254)
|
|
* returns WS_SUCCESS on success */
|
|
int SendChannelTerminalRequest(WOLFSSH* ssh)
|
|
{
|
|
byte* output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
WOLFSSH_CHANNEL* channel;
|
|
const char cType[] = "pty-req";
|
|
const char envVar[] = "xterm";
|
|
byte mode[4096];
|
|
word32 envSz, typeSz, modeSz;
|
|
word32 w = 80, h = 24;
|
|
word32 pxW = 0, pxH = 0;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendChannelTerminalRequest()");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
envSz = (word32)WSTRLEN(envVar);
|
|
typeSz = (word32)WSTRLEN(cType);
|
|
modeSz = CreateMode(ssh, mode);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
channel = ChannelFind(ssh,
|
|
ssh->defaultPeerChannelId, WS_CHANNEL_ID_PEER);
|
|
if (channel == NULL)
|
|
ret = WS_INVALID_CHANID;
|
|
}
|
|
|
|
/* craft packet with the following structure
|
|
* byte MSGID_CHANNEL_REQUEST
|
|
* word32 channel
|
|
* string "pty-req"
|
|
* boolean want_reply
|
|
* string term environment variable
|
|
* word32 terminal width
|
|
* word32 terminal height
|
|
* word32 terminal width (pixels)
|
|
* word32 terminal height (pixels)
|
|
* string encoded terminal modes
|
|
*/
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ + LENGTH_SZ +
|
|
typeSz + BOOLEAN_SZ +
|
|
((envSz > 0)? UINT32_SZ : 0) + envSz +
|
|
UINT32_SZ * 4 +
|
|
((modeSz > 0)? UINT32_SZ : 0) + modeSz);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_CHANNEL_REQUEST;
|
|
c32toa(channel->peerChannel, output + idx); idx += UINT32_SZ;
|
|
c32toa(typeSz, output + idx); idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, cType, typeSz); idx += typeSz;
|
|
output[idx++] = 1; /* want reply */
|
|
|
|
if (envSz > 0) {
|
|
c32toa(envSz, output + idx); idx += UINT32_SZ;
|
|
WMEMCPY(output + idx, envVar, envSz); idx += envSz;
|
|
}
|
|
|
|
c32toa(w, output + idx); idx += UINT32_SZ;
|
|
c32toa(h, output + idx); idx += UINT32_SZ;
|
|
c32toa(pxW, output + idx); idx += UINT32_SZ;
|
|
c32toa(pxH, output + idx); idx += UINT32_SZ;
|
|
|
|
if (modeSz > 0) {
|
|
c32toa(modeSz, output + idx); idx += UINT32_SZ;
|
|
WMEMCPY(output + idx, mode, modeSz); idx += modeSz;
|
|
}
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
WLOG(WS_LOG_INFO, "Sending Pseudo-Terminal Channel Request: ");
|
|
WLOG(WS_LOG_INFO, " channelId = %u", channel->peerChannel);
|
|
WLOG(WS_LOG_INFO, " type = %s", cType);
|
|
WLOG(WS_LOG_INFO, " wantReply = %u", 1);
|
|
WLOG(WS_LOG_INFO, " (width , height) = (%d , %d)", w, h);
|
|
WLOG(WS_LOG_INFO, " pixels (width , height) = (%d , %d)", pxW, pxH);
|
|
WLOG(WS_LOG_INFO, " term mode = %s", mode);
|
|
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelTerminalRequest(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSH_TERM */
|
|
|
|
|
|
#ifdef WOLFSSH_AGENT
|
|
|
|
int SendChannelAgentRequest(WOLFSSH* ssh)
|
|
{
|
|
byte* output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
WOLFSSH_CHANNEL* channel;
|
|
const char* cType = "auth-agent-req@openssh.com";
|
|
word32 typeSz;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendChannelRequestAgent()");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
channel = ChannelFind(ssh,
|
|
ssh->defaultPeerChannelId, WS_CHANNEL_ID_PEER);
|
|
if (channel == NULL)
|
|
ret = WS_INVALID_CHANID;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
typeSz = (word32)WSTRLEN(cType);
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ + LENGTH_SZ +
|
|
typeSz + BOOLEAN_SZ);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = MSGID_CHANNEL_REQUEST;
|
|
c32toa(channel->peerChannel, output + idx);
|
|
idx += UINT32_SZ;
|
|
c32toa(typeSz, output + idx);
|
|
idx += LENGTH_SZ;
|
|
WMEMCPY(output + idx, cType, typeSz);
|
|
idx += typeSz;
|
|
output[idx++] = 0;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
WLOG(WS_LOG_INFO, "Sending Channel Request: ");
|
|
WLOG(WS_LOG_INFO, " channelId = %u", channel->peerChannel);
|
|
WLOG(WS_LOG_INFO, " type = %s", cType);
|
|
WLOG(WS_LOG_INFO, " wantReply = %u", 0);
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelRequestAgent(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
#endif /* WOLFSSH_AGENT */
|
|
|
|
|
|
int SendChannelSuccess(WOLFSSH* ssh, word32 channelId, int success)
|
|
{
|
|
byte* output;
|
|
word32 idx;
|
|
int ret = WS_SUCCESS;
|
|
WOLFSSH_CHANNEL* channel = NULL;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Entering SendChannelSuccess(), %s",
|
|
success ? "Success" : "Failure");
|
|
|
|
if (ssh == NULL)
|
|
ret = WS_BAD_ARGUMENT;
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF);
|
|
if (channel == NULL) {
|
|
WLOG(WS_LOG_DEBUG, "Invalid channel");
|
|
ret = WS_INVALID_CHANID;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ);
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
output = ssh->outputBuffer.buffer;
|
|
idx = ssh->outputBuffer.length;
|
|
|
|
output[idx++] = success ?
|
|
MSGID_CHANNEL_SUCCESS : MSGID_CHANNEL_FAILURE;
|
|
c32toa(channel->peerChannel, output + idx);
|
|
idx += UINT32_SZ;
|
|
|
|
ssh->outputBuffer.length = idx;
|
|
|
|
ret = BundlePacket(ssh);
|
|
}
|
|
|
|
if (ret == WS_SUCCESS)
|
|
ret = wolfSSH_SendPacket(ssh);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Leaving SendChannelSuccess(), ret = %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
#if (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP)) && \
|
|
!defined(NO_WOLFSSH_SERVER)
|
|
/* cleans up absolute path
|
|
* returns size of new path on success (strlen sz) and negative values on fail*/
|
|
int wolfSSH_CleanPath(WOLFSSH* ssh, char* in)
|
|
{
|
|
int i;
|
|
long sz;
|
|
byte found;
|
|
char *path;
|
|
|
|
if (in == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
sz = (long)WSTRLEN(in);
|
|
path = (char*)WMALLOC(sz+1, ssh->ctx->heap, DYNTYPE_PATH);
|
|
if (path == NULL) {
|
|
return WS_MEMORY_E;
|
|
}
|
|
WMEMCPY(path, in, sz);
|
|
path[sz] = '\0';
|
|
|
|
#if defined(WOLFSSL_NUCLEUS) || defined(USE_WINDOWS_API)
|
|
for (i = 0; i < sz; i++) {
|
|
if (path[i] == '/') path[i] = '\\';
|
|
}
|
|
#endif
|
|
sz = (long)WSTRLEN(path);
|
|
|
|
/* remove any /./ patterns, direcotries, exclude cases like ./ok./test */
|
|
for (i = 1; i + 1 < sz; i++) {
|
|
if (path[i] == '.' && path[i - 1] == WS_DELIM && path[i + 1] == WS_DELIM) {
|
|
WMEMMOVE(path + i, path + i + 1, sz - (i + 1));
|
|
sz -= 1;
|
|
i--;
|
|
}
|
|
}
|
|
|
|
/* remove any double '/' or '\' chars */
|
|
for (i = 0; i < sz; i++) {
|
|
if ((path[i] == WS_DELIM && path[i+1] == WS_DELIM)) {
|
|
WMEMMOVE(path + i, path + i + 1, sz - i);
|
|
sz -= 1;
|
|
i--;
|
|
}
|
|
}
|
|
|
|
if (path != NULL) {
|
|
/* go through path until no cases are found */
|
|
do {
|
|
int prIdx = 0; /* begin of cut */
|
|
int enIdx = 0; /* end of cut */
|
|
sz = (long)WSTRLEN(path);
|
|
|
|
found = 0;
|
|
for (i = 1; i < sz; i++) {
|
|
if (path[i] == WS_DELIM) {
|
|
int z;
|
|
|
|
/* if next two chars are .. then delete */
|
|
if (path[i+1] == '.' && path[i+2] == '.') {
|
|
enIdx = i + 3;
|
|
|
|
/* start at one char before / and retrace path */
|
|
for (z = i - 1; z > 0; z--) {
|
|
if (path[z] == WS_DELIM || path[z] == ':') {
|
|
prIdx = z;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* cut out .. and previous */
|
|
WMEMMOVE(path + prIdx, path + enIdx, sz - enIdx);
|
|
path[sz - (enIdx - prIdx)] = '\0';
|
|
|
|
if (enIdx == sz) {
|
|
path[prIdx] = '\0';
|
|
}
|
|
|
|
/* case of at / */
|
|
if (WSTRLEN(path) == 0) {
|
|
path[0] = '/';
|
|
path[1] = '\0';
|
|
}
|
|
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} while (found);
|
|
|
|
#if defined(WOLFSSL_NUCLEUS) || defined(USE_WINDOWS_API)
|
|
sz = (long)WSTRLEN(path);
|
|
|
|
if (path[sz - 1] == ':') {
|
|
path[sz] = WS_DELIM;
|
|
path[sz + 1] = '\0';
|
|
}
|
|
|
|
/* clean up any multiple drive listed i.e. A:/A: */
|
|
{
|
|
int i,j;
|
|
sz = (long)WSTRLEN(path);
|
|
for (i = 0, j = 0; i < sz; i++) {
|
|
if (path[i] == ':') {
|
|
if (j == 0) j = i;
|
|
else {
|
|
/* @TODO only checking once */
|
|
WMEMMOVE(path, path + i - WS_DRIVE_SIZE,
|
|
sz - i + WS_DRIVE_SIZE);
|
|
path[sz - i + WS_DRIVE_SIZE] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* remove leading '/' for nucleus. Preserve case of single "/" */
|
|
sz = (long)WSTRLEN(path);
|
|
while (sz > 2 && path[0] == WS_DELIM) {
|
|
sz--;
|
|
WMEMMOVE(path, path + 1, sz);
|
|
path[sz] = '\0';
|
|
}
|
|
#endif
|
|
|
|
#ifndef FREESCALE_MQX
|
|
/* remove trailing delimiter */
|
|
if (sz > 3 && path[sz - 1] == WS_DELIM) {
|
|
path[sz - 1] = '\0';
|
|
}
|
|
#endif
|
|
|
|
#ifdef FREESCALE_MQX
|
|
/* remove trailing '.' */
|
|
if (path[sz - 1] == '.') {
|
|
path[sz - 1] = '\0';
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* copy result back to 'in' buffer */
|
|
if (WSTRLEN(in) < WSTRLEN(path)) {
|
|
WLOG(WS_LOG_ERROR, "Fatal error cleaning path");
|
|
WFREE(path, ssh->ctx->heap, DYNTYPE_PATH);
|
|
return WS_BUFFER_E;
|
|
}
|
|
sz = (long)WSTRLEN(path);
|
|
WMEMCPY(in, path, sz);
|
|
in[sz] = '\0';
|
|
WFREE(path, ssh->ctx->heap, DYNTYPE_PATH);
|
|
return (int)sz;
|
|
}
|
|
#endif /* WOLFSSH_SFTP || WOLFSSH_SCP */
|
|
|
|
|
|
#ifdef DEBUG_WOLFSSH
|
|
|
|
#define LINE_WIDTH 16
|
|
void DumpOctetString(const byte* input, word32 inputSz)
|
|
{
|
|
int rows = inputSz / LINE_WIDTH;
|
|
int remainder = inputSz % LINE_WIDTH;
|
|
int i,j;
|
|
char text[17];
|
|
byte c;
|
|
|
|
for (i = 0; i < rows; i++) {
|
|
XMEMSET(text, 0, sizeof text);
|
|
printf("%04X: ", i * LINE_WIDTH);
|
|
for (j = 0; j < LINE_WIDTH; j++) {
|
|
c = input[i * LINE_WIDTH + j];
|
|
printf("%02X ", c);
|
|
text[j] = isprint(c) ? (char)c : '.';
|
|
}
|
|
printf(" %s\n", text);
|
|
}
|
|
if (remainder) {
|
|
XMEMSET(text, 0, sizeof text);
|
|
printf("%04X: ", i * LINE_WIDTH);
|
|
for (j = 0; j < remainder; j++) {
|
|
c = input[i * LINE_WIDTH + j];
|
|
printf("%02X ", c);
|
|
text[j] = isprint(c) ? c : '.';
|
|
}
|
|
for (; j < LINE_WIDTH; j++) {
|
|
printf(" ");
|
|
}
|
|
printf(" %s\n", text);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef WOLFSSH_SFTP
|
|
|
|
/* converts the octal input to decimal. Input is in string format i.e. 0666
|
|
* returns the decimal value on success or negative value on failure */
|
|
int wolfSSH_oct2dec(WOLFSSH* ssh, byte* oct, word32 octSz)
|
|
{
|
|
int ret;
|
|
word32 i;
|
|
|
|
if (octSz > WOLFSSH_MAX_OCTET_LEN || ssh == NULL || oct == NULL) {
|
|
return WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
/* convert octal string to int without mp_read_radix() */
|
|
ret = 0;
|
|
|
|
for (i = 0; i < octSz; i++)
|
|
{
|
|
if (oct[i] < '0' || oct[0] > '7') {
|
|
ret = WS_BAD_ARGUMENT;
|
|
break;
|
|
}
|
|
ret <<= 3;
|
|
ret |= (oct[i] - '0');
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* addend1 += addend2 */
|
|
void AddAssign64(word32* addend1, word32 addend2)
|
|
{
|
|
word32 tmp = addend1[0];
|
|
if ((addend1[0] += addend2) < tmp)
|
|
addend1[1]++;
|
|
}
|
|
|
|
#endif /* WOLFSSH_SFTP */
|