mirror of https://github.com/wolfSSL/wolfssh.git
Moved functions to internal.c that belong there.
parent
e03a1011d2
commit
a96a764bc0
452
src/internal.c
452
src/internal.c
|
@ -29,6 +29,13 @@
|
|||
#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];
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t id;
|
||||
const char* name;
|
||||
|
@ -185,3 +192,448 @@ void ShrinkBuffer(Buffer* buf)
|
|||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
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* list, uint8_t* listSz,
|
||||
uint8_t* buf, uint32_t len, uint32_t* idx)
|
||||
{
|
||||
uint8_t i = 0;
|
||||
uint32_t nameListSz;
|
||||
uint32_t begin = *idx;
|
||||
(void)list;
|
||||
|
||||
if (begin >= len || begin + 4 >= len)
|
||||
return -1;
|
||||
|
||||
ato32(buf + begin, &nameListSz);
|
||||
begin += 4;
|
||||
if (begin + nameListSz > len)
|
||||
return -1;
|
||||
|
||||
begin += nameListSz;
|
||||
/* list[0] = NameToId(nextName, 0); */
|
||||
|
||||
*listSz = i;
|
||||
*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 */
|
||||
listSz = 2;
|
||||
DoNameList(list, &listSz, buf, len, &begin);
|
||||
|
||||
/* Server Host Key Algorithms */
|
||||
listSz = 1;
|
||||
DoNameList(list, &listSz, buf, len, &begin);
|
||||
|
||||
/* Enc Algorithms - Client to Server */
|
||||
listSz = 3;
|
||||
DoNameList(list, &listSz, buf, len, &begin);
|
||||
|
||||
/* Enc Algorithms - Server to Client */
|
||||
listSz = 3;
|
||||
DoNameList(list, &listSz, buf, len, &begin);
|
||||
|
||||
/* MAC Algorithms - Client to Server */
|
||||
listSz = 2;
|
||||
DoNameList(list, &listSz, buf, len, &begin);
|
||||
|
||||
/* MAC Algorithms - Server to Client */
|
||||
listSz = 2;
|
||||
DoNameList(list, &listSz, buf, len, &begin);
|
||||
|
||||
/* Compression Algorithms - Client to Server */
|
||||
listSz = 1;
|
||||
DoNameList(list, &listSz, buf, len, &begin);
|
||||
/* verify the list contains "none" */
|
||||
|
||||
/* 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->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;
|
||||
}
|
||||
|
||||
ssh->peerId = (char*)WMALLOC(ssh->inputBuffer->length-1, ssh->ctx->heap, WOLFSSH_ID_TYPE);
|
||||
if (ssh->peerId == NULL) {
|
||||
return WS_MEMORY_E;
|
||||
}
|
||||
|
||||
WMEMCPY(ssh->peerId, ssh->inputBuffer->buffer, ssh->inputBuffer->length-2);
|
||||
ssh->peerId[ssh->inputBuffer->length - 1] = 0;
|
||||
ssh->inputBuffer->idx += ssh->inputBuffer->length;
|
||||
WLOG(WS_LOG_DEBUG, "%s", ssh->peerId);
|
||||
|
||||
return WS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int SendServerVersion(WOLFSSH* ssh)
|
||||
{
|
||||
(void)ssh;
|
||||
|
||||
WLOG(WS_LOG_DEBUG, "%s", sshIdStr);
|
||||
SendText(ssh, sshIdStr, (uint32_t)WSTRLEN(sshIdStr));
|
||||
|
||||
return WS_FATAL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
|
453
src/ssh.c
453
src/ssh.c
|
@ -29,13 +29,6 @@
|
|||
#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];
|
||||
}
|
||||
|
||||
|
||||
int wolfSSH_Init(void)
|
||||
{
|
||||
WLOG(WS_LOG_DEBUG, "Enter wolfSSH_Init()");
|
||||
|
@ -216,405 +209,6 @@ int wolfSSH_get_fd(const WOLFSSH* ssh)
|
|||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
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* list, uint8_t* listSz,
|
||||
uint8_t* buf, uint32_t len, uint32_t* idx)
|
||||
{
|
||||
uint8_t i = 0;
|
||||
uint32_t nameListSz;
|
||||
uint32_t begin = *idx;
|
||||
(void)list;
|
||||
|
||||
if (begin >= len || begin + 4 >= len)
|
||||
return -1;
|
||||
|
||||
ato32(buf + begin, &nameListSz);
|
||||
begin += 4;
|
||||
if (begin + nameListSz > len)
|
||||
return -1;
|
||||
|
||||
begin += nameListSz;
|
||||
/* list[0] = NameToId(nextName, 0); */
|
||||
|
||||
*listSz = i;
|
||||
*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 */
|
||||
listSz = 2;
|
||||
DoNameList(list, &listSz, buf, len, &begin);
|
||||
|
||||
/* Server Host Key Algorithms */
|
||||
listSz = 1;
|
||||
DoNameList(list, &listSz, buf, len, &begin);
|
||||
|
||||
/* Enc Algorithms - Client to Server */
|
||||
listSz = 3;
|
||||
DoNameList(list, &listSz, buf, len, &begin);
|
||||
|
||||
/* Enc Algorithms - Server to Client */
|
||||
listSz = 3;
|
||||
DoNameList(list, &listSz, buf, len, &begin);
|
||||
|
||||
/* MAC Algorithms - Client to Server */
|
||||
listSz = 2;
|
||||
DoNameList(list, &listSz, buf, len, &begin);
|
||||
|
||||
/* MAC Algorithms - Server to Client */
|
||||
listSz = 2;
|
||||
DoNameList(list, &listSz, buf, len, &begin);
|
||||
|
||||
/* Compression Algorithms - Client to Server */
|
||||
listSz = 1;
|
||||
DoNameList(list, &listSz, buf, len, &begin);
|
||||
/* verify the list contains "none" */
|
||||
|
||||
/* 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->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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int wolfSSH_accept(WOLFSSH* ssh)
|
||||
{
|
||||
switch (ssh->acceptState) {
|
||||
|
@ -647,50 +241,3 @@ int wolfSSH_accept(WOLFSSH* ssh)
|
|||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ssh->peerId = (char*)WMALLOC(ssh->inputBuffer->length-1, ssh->ctx->heap, WOLFSSH_ID_TYPE);
|
||||
if (ssh->peerId == NULL) {
|
||||
return WS_MEMORY_E;
|
||||
}
|
||||
|
||||
WMEMCPY(ssh->peerId, ssh->inputBuffer->buffer, ssh->inputBuffer->length-2);
|
||||
ssh->peerId[ssh->inputBuffer->length - 1] = 0;
|
||||
ssh->inputBuffer->idx += ssh->inputBuffer->length;
|
||||
WLOG(WS_LOG_DEBUG, "%s", ssh->peerId);
|
||||
|
||||
return WS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int SendServerVersion(WOLFSSH* ssh)
|
||||
{
|
||||
(void)ssh;
|
||||
|
||||
WLOG(WS_LOG_DEBUG, "%s", sshIdStr);
|
||||
SendText(ssh, sshIdStr, (uint32_t)WSTRLEN(sshIdStr));
|
||||
|
||||
return WS_FATAL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -142,8 +142,8 @@ WOLFSSH_LOCAL int wsEmbedSend(WOLFSSH* ssh, void*, uint32_t sz, void* ctx);
|
|||
|
||||
|
||||
WOLFSSH_LOCAL int ProcessReply(WOLFSSH*);
|
||||
WOLFSSH_LOCAL int ProcessClientVersion(WOLFSSH*);
|
||||
WOLFSSH_LOCAL int SendServerVersion(WOLFSSH*);
|
||||
WOLFSSH_LOCAL int DoClientVersion(WOLFSSH*);
|
||||
|
||||
|
||||
enum AcceptStates {
|
||||
|
|
Loading…
Reference in New Issue