mirror of https://github.com/wolfSSL/wolfssh.git
745 lines
20 KiB
C
745 lines
20 KiB
C
/* internal.c
|
|
*
|
|
* Copyright (C) 2014 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 2 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 this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
|
|
/*
|
|
* 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>
|
|
|
|
|
|
/* convert opaque to 32 bit integer */
|
|
static /*INLINE*/ void ato32(const uint8_t* c, uint32_t* u32)
|
|
{
|
|
*u32 = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
|
|
}
|
|
|
|
|
|
const char* GetErrorString(int err)
|
|
{
|
|
#ifdef NO_WOLFSSH_STRINGS
|
|
return "No wolfSSH strings available";
|
|
#else
|
|
switch (err) {
|
|
case WS_SUCCESS:
|
|
return "function success";
|
|
|
|
case WS_FATAL_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";
|
|
|
|
default:
|
|
return "Unknown error code";
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
uint8_t id;
|
|
const char* name;
|
|
} NameIdPair;
|
|
|
|
|
|
static const NameIdPair NameIdMap[] = {
|
|
{ ID_NONE, "none" },
|
|
{ ID_AES128_CBC, "aes128-cbc" },
|
|
{ ID_AES128_CTR, "aes128-ctr" },
|
|
{ ID_AES128_GCM_WOLF, "aes128-gcm@wolfssl.com" },
|
|
{ ID_HMAC_SHA1, "hmac-sha1" },
|
|
{ ID_HMAC_SHA1_96, "hmac-sha1-96" },
|
|
{ ID_DH_GROUP1_SHA1, "diffie-hellman-group1-sha1" },
|
|
{ ID_DH_GROUP14_SHA1, "diffie-hellman-group14-sha1" },
|
|
{ ID_SSH_RSA, "ssh-rsa" }
|
|
};
|
|
|
|
|
|
uint8_t NameToId(const char* name, uint32_t nameSz)
|
|
{
|
|
uint8_t id = ID_UNKNOWN;
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < (sizeof(NameIdMap)/sizeof(NameIdPair)); i++) {
|
|
if (nameSz == WSTRLEN(NameIdMap[i].name) &&
|
|
WSTRNCMP(name, NameIdMap[i].name, nameSz) == 0) {
|
|
|
|
id = NameIdMap[i].id;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
|
|
const char* IdToName(uint8_t id)
|
|
{
|
|
const char* name = NULL;
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < (sizeof(NameIdMap)/sizeof(NameIdPair)); i++) {
|
|
if (NameIdMap[i].id == id) {
|
|
name = NameIdMap[i].name;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
|
|
int BufferInit(Buffer* buffer, uint32_t 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 = (uint8_t*)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, uint32_t sz, uint32_t usedSz)
|
|
{
|
|
WLOG(WS_LOG_DEBUG, "GB: buf = %p", buf);
|
|
WLOG(WS_LOG_DEBUG, "GB: sz = %d", sz);
|
|
WLOG(WS_LOG_DEBUG, "GB: usedSz = %d", usedSz);
|
|
/* New buffer will end up being sz+usedSz long
|
|
* empty space at the head of the buffer will be compressed */
|
|
if (buf != NULL) {
|
|
uint32_t newSz = sz + usedSz;
|
|
WLOG(WS_LOG_DEBUG, "GB: newSz = %d", newSz);
|
|
|
|
if (newSz > buf->bufferSz) {
|
|
uint8_t* newBuffer = (uint8_t*)WMALLOC(newSz,
|
|
buf->heap, DYNTYPE_BUFFER);
|
|
|
|
WLOG(WS_LOG_DEBUG, "Growing buffer");
|
|
|
|
if (newBuffer == NULL)
|
|
return WS_MEMORY_E;
|
|
|
|
WLOG(WS_LOG_DEBUG, "GB: resizing buffer");
|
|
if (buf->length > 0)
|
|
WMEMCPY(newBuffer, buf->buffer + buf->idx, buf->length);
|
|
|
|
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)
|
|
{
|
|
if (buf != NULL) {
|
|
uint32_t usedSz = buf->length - buf->idx;
|
|
|
|
if (!forcedFree && usedSz > STATIC_BUFFER_LEN)
|
|
return;
|
|
|
|
WLOG(WS_LOG_DEBUG, "Shrinking buffer");
|
|
|
|
if (!forcedFree && usedSz)
|
|
WMEMCPY(buf->staticBuffer, buf->buffer + buf->idx, usedSz);
|
|
|
|
if (buf->dynamicFlag)
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static int Receive(WOLFSSH* ssh, uint8_t* buf, uint32_t 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)
|
|
{
|
|
int gotLine = 0;
|
|
int inSz = 255;
|
|
int in;
|
|
|
|
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;
|
|
|
|
if (ssh->inputBuffer.length > 2) {
|
|
if (ssh->inputBuffer.buffer[ssh->inputBuffer.length - 2] == '\r' &&
|
|
ssh->inputBuffer.buffer[ssh->inputBuffer.length - 1] == '\n') {
|
|
|
|
gotLine = 1;
|
|
}
|
|
}
|
|
} while (!gotLine);
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
static int SendBuffer(WOLFSSH* ssh)
|
|
{
|
|
if (ssh->ctx->ioSendCb == NULL) {
|
|
WLOG(WS_LOG_DEBUG, "Your IO Send callback is null, please set");
|
|
return -1;
|
|
}
|
|
|
|
while (ssh->outputBuffer.length > 0) {
|
|
int sent = ssh->ctx->ioSendCb(ssh,
|
|
ssh->outputBuffer.buffer + ssh->outputBuffer.idx,
|
|
ssh->outputBuffer.length, ssh->ioWriteCtx);
|
|
|
|
if (sent < 0) {
|
|
return WS_SOCKET_ERROR_E;
|
|
}
|
|
|
|
if (sent > (int)ssh->outputBuffer.length) {
|
|
WLOG(WS_LOG_DEBUG, "Out of bounds read");
|
|
return WS_SEND_OOB_READ_E;
|
|
}
|
|
|
|
ssh->outputBuffer.idx += sent;
|
|
ssh->outputBuffer.length -= sent;
|
|
}
|
|
|
|
ssh->outputBuffer.idx = 0;
|
|
ShrinkBuffer(&ssh->outputBuffer, 0);
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
static int SendText(WOLFSSH* ssh, const char* text, uint32_t textLen)
|
|
{
|
|
GrowBuffer(&ssh->outputBuffer, textLen, 0);
|
|
WMEMCPY(ssh->outputBuffer.buffer, text, textLen);
|
|
ssh->outputBuffer.length = textLen;
|
|
|
|
return SendBuffer(ssh);
|
|
}
|
|
|
|
|
|
static int GetInputData(WOLFSSH* ssh, uint32_t 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 */
|
|
|
|
WLOG(WS_LOG_DEBUG, "GID: size = %d", 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);
|
|
|
|
/*
|
|
* 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_BUFFER_E;
|
|
|
|
/*
|
|
* 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)
|
|
return WS_MEMORY_E;
|
|
}
|
|
|
|
/* 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)
|
|
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;
|
|
|
|
} while (ssh->inputBuffer.length < size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int DoNameList(uint8_t* idList, uint8_t* idListSz,
|
|
uint8_t* buf, uint32_t len, uint32_t* idx)
|
|
{
|
|
uint8_t idListIdx;
|
|
uint32_t nameListSz, nameListIdx;
|
|
uint32_t begin = *idx;
|
|
uint8_t* name;
|
|
uint32_t nameSz;
|
|
|
|
/*
|
|
* This iterates across a name list and finds names that end in either the
|
|
* comma delimeter or with the end of the list.
|
|
*/
|
|
|
|
if (begin >= len || begin + 4 >= len)
|
|
return -1;
|
|
|
|
ato32(buf + begin, &nameListSz);
|
|
begin += 4;
|
|
if (begin + nameListSz > len)
|
|
return -1;
|
|
|
|
/* 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. */
|
|
name = buf + begin;
|
|
nameSz = 0;
|
|
nameListIdx = 0;
|
|
idListIdx = 0;
|
|
|
|
while (nameListIdx < nameListSz) {
|
|
nameListIdx++;
|
|
|
|
if (nameListIdx == nameListSz)
|
|
nameSz++;
|
|
|
|
if (nameListIdx == nameListSz || name[nameSz] == ',') {
|
|
uint8_t 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)
|
|
idList[idListIdx++] = id;
|
|
|
|
name += 1 + nameSz;
|
|
nameSz = 0;
|
|
}
|
|
else
|
|
nameSz++;
|
|
}
|
|
|
|
begin += nameListSz;
|
|
*idListSz = idListIdx;
|
|
*idx = begin;
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
static int DoKexInit(WOLFSSH* ssh, uint8_t* buf, uint32_t len, uint32_t* idx)
|
|
{
|
|
uint8_t list[3];
|
|
uint8_t listSz;
|
|
uint32_t skipSz;
|
|
uint32_t begin = *idx;
|
|
|
|
/*
|
|
* 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.
|
|
*
|
|
* Save the cookie for now. Maybe that is used in KEX.
|
|
*
|
|
* byte[16] cookie
|
|
* name-list kex_algorithms (2)
|
|
* name-list server_host_key_algorithms (1)
|
|
* name-list encryption_algorithms_client_to_server (3)
|
|
* name-list encryption_algorithms_server_to_client (3)
|
|
* name-list mac_algorithms_client_to_server (2)
|
|
* name-list mac_algorithms_server_to_client (2)
|
|
* name-list compression_algorithms_client_to_server (1)
|
|
* name-list compression_algorithms_server_to_client (1)
|
|
* name-list languages_client_to_server (0, skip)
|
|
* name-list languages_server_to_client (0, skip)
|
|
* boolean first_kex_packet_follows
|
|
* uint32 0 (reserved for future extension)
|
|
*/
|
|
|
|
/* Check that the cookie exists inside the message */
|
|
if (begin + COOKIE_SZ > len) {
|
|
/* error, out of bounds */
|
|
return -1;
|
|
}
|
|
/* Move past the cookie. */
|
|
begin += COOKIE_SZ;
|
|
|
|
/* KEX Algorithms */
|
|
WLOG(WS_LOG_DEBUG, "DKI: KEX Algorithms");
|
|
listSz = 2;
|
|
DoNameList(list, &listSz, buf, len, &begin);
|
|
|
|
/* Server Host Key Algorithms */
|
|
WLOG(WS_LOG_DEBUG, "DKI: Server Host Key Algorithms");
|
|
listSz = 1;
|
|
DoNameList(list, &listSz, buf, len, &begin);
|
|
|
|
/* Enc Algorithms - Client to Server */
|
|
WLOG(WS_LOG_DEBUG, "DKI: Enc Algorithms - Client to Server");
|
|
listSz = 3;
|
|
DoNameList(list, &listSz, buf, len, &begin);
|
|
|
|
/* Enc Algorithms - Server to Client */
|
|
WLOG(WS_LOG_DEBUG, "DKI: Enc Algorithms - Server to Client");
|
|
listSz = 3;
|
|
DoNameList(list, &listSz, buf, len, &begin);
|
|
|
|
/* MAC Algorithms - Client to Server */
|
|
WLOG(WS_LOG_DEBUG, "DKI: MAC Algorithms - Client to Server");
|
|
listSz = 2;
|
|
DoNameList(list, &listSz, buf, len, &begin);
|
|
|
|
/* MAC Algorithms - Server to Client */
|
|
WLOG(WS_LOG_DEBUG, "DKI: MAC Algorithms - Server to Client");
|
|
listSz = 2;
|
|
DoNameList(list, &listSz, buf, len, &begin);
|
|
|
|
/* Compression Algorithms - Client to Server */
|
|
WLOG(WS_LOG_DEBUG, "DKI: Compression Algorithms - Client to Server");
|
|
listSz = 1;
|
|
DoNameList(list, &listSz, buf, len, &begin);
|
|
/* verify the list contains "none" */
|
|
|
|
/* Compression Algorithms - Server to Client */
|
|
WLOG(WS_LOG_DEBUG, "DKI: Compression Algorithms - Server to Client");
|
|
listSz = 1;
|
|
DoNameList(list, &listSz, buf, len, &begin);
|
|
/* verify the list contains "none" */
|
|
|
|
/* Languages - Client to Server, skip */
|
|
ato32(buf + begin, &skipSz);
|
|
begin += 4 + skipSz;
|
|
|
|
/* Languages - Server to Client, skip */
|
|
ato32(buf + begin, &skipSz);
|
|
begin += 4 + skipSz;
|
|
|
|
/* First KEX Packet Follows */
|
|
ssh->handshake->kexPacketFollows = buf[begin];
|
|
begin += 1;
|
|
|
|
/* Skip the "for future use" length. */
|
|
ato32(buf + begin, &skipSz);
|
|
begin += 4 + skipSz;
|
|
|
|
*idx = begin;
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
static int DoPacket(WOLFSSH* ssh)
|
|
{
|
|
uint8_t* buf = (uint8_t*)ssh->inputBuffer.buffer;
|
|
uint32_t idx = ssh->inputBuffer.idx;
|
|
uint32_t len = ssh->inputBuffer.length;
|
|
uint8_t msg;
|
|
uint8_t padSz;
|
|
|
|
padSz = buf[idx++];
|
|
|
|
msg = buf[idx++];
|
|
switch (msg) {
|
|
|
|
case SSH_MSG_KEXINIT:
|
|
WLOG(WS_LOG_DEBUG, "Decoding SSH_MSG_KEXINIT (len = %d)", len);
|
|
DoKexInit(ssh, buf, len, &idx);
|
|
break;
|
|
|
|
default:
|
|
WLOG(WS_LOG_DEBUG, "Unsupported message ID (%d)", msg);
|
|
break;
|
|
}
|
|
|
|
if (idx + padSz > len) {
|
|
return -1;
|
|
}
|
|
idx += padSz;
|
|
|
|
ssh->inputBuffer.idx = idx;
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
int ProcessReply(WOLFSSH* ssh)
|
|
{
|
|
int ret = WS_FATAL_ERROR;
|
|
int readSz;
|
|
|
|
(void)readSz;
|
|
for (;;) {
|
|
switch (ssh->processReplyState) {
|
|
case PROCESS_INIT:
|
|
readSz = ssh->blockSz;
|
|
WLOG(WS_LOG_DEBUG, "PR1: size = %d", readSz);
|
|
if ((ret = GetInputData(ssh, readSz)) < 0) {
|
|
return ret;
|
|
}
|
|
ssh->processReplyState = PROCESS_PACKET_LENGTH;
|
|
|
|
/* Decrypt first block if encrypted */
|
|
|
|
case PROCESS_PACKET_LENGTH:
|
|
ato32(ssh->inputBuffer.buffer + ssh->inputBuffer.idx, &ssh->curSz);
|
|
ssh->inputBuffer.idx += LENGTH_SZ;
|
|
ssh->processReplyState = PROCESS_PACKET_FINISH;
|
|
|
|
case PROCESS_PACKET_FINISH:
|
|
WLOG(WS_LOG_DEBUG, "PR2: size = %d", ssh->curSz);
|
|
if ((ret = GetInputData(ssh, ssh->curSz)) < 0) {
|
|
|
|
return ret;
|
|
}
|
|
ssh->processReplyState = PROCESS_PACKET;
|
|
|
|
/* Decrypt rest of packet here */
|
|
|
|
/* Check MAC here. */
|
|
|
|
case PROCESS_PACKET:
|
|
if ( (ret = DoPacket(ssh)) < 0) {
|
|
return ret;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
WLOG(WS_LOG_DEBUG, "Bad process input state, programming error");
|
|
return WS_INPUT_CASE_E;
|
|
}
|
|
ssh->processReplyState = PROCESS_INIT;
|
|
return WS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
static const char sshIdStr[] = "SSH-2.0-wolfSSHv" LIBWOLFSSH_VERSION_STRING "\r\n";
|
|
|
|
|
|
int ProcessClientVersion(WOLFSSH* ssh)
|
|
{
|
|
int error;
|
|
size_t protoLen = 7; /* Length of the SSH-2.0 portion of the ID str */
|
|
|
|
if ( (error = GetInputText(ssh)) < 0) {
|
|
WLOG(WS_LOG_DEBUG, "get input text failed");
|
|
return error;
|
|
}
|
|
|
|
if (WSTRNCASECMP((char*)ssh->inputBuffer.buffer,
|
|
sshIdStr, protoLen) == 0) {
|
|
ssh->clientState = CLIENT_VERSION_DONE;
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_DEBUG, "SSH version mismatch");
|
|
return WS_VERSION_E;
|
|
}
|
|
|
|
ShaUpdate(&ssh->handshake->hash, ssh->inputBuffer.buffer,
|
|
ssh->inputBuffer.length - 2);
|
|
ssh->inputBuffer.idx += ssh->inputBuffer.length;
|
|
|
|
return WS_SUCCESS;
|
|
}
|
|
|
|
|
|
int SendServerVersion(WOLFSSH* ssh)
|
|
{
|
|
uint32_t sshIdStrSz = (uint32_t)WSTRLEN(sshIdStr);
|
|
|
|
WLOG(WS_LOG_DEBUG, "%s", sshIdStr);
|
|
SendText(ssh, sshIdStr, (uint32_t)WSTRLEN(sshIdStr));
|
|
ShaUpdate(&ssh->handshake->hash, (const uint8_t*)sshIdStr, sshIdStrSz);
|
|
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
|