From b578aadc6d7bb2bffc4c7f813ca2608df432e795 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Tue, 23 Jan 2018 12:22:25 -0800 Subject: [PATCH] SCP Contribution --- src/internal.c | 255 ++++++++++++++++++++++++++++++++++++++++----- src/ssh.c | 63 ++++++++++- wolfssh/error.h | 1 + wolfssh/internal.h | 11 +- wolfssh/ssh.h | 12 +++ 5 files changed, 314 insertions(+), 28 deletions(-) diff --git a/src/internal.c b/src/internal.c index 36bbeea..a03f48b 100644 --- a/src/internal.c +++ b/src/internal.c @@ -51,6 +51,7 @@ static const char sshProtoIdStr[] = "SSH-2.0-wolfSSHv" LIBWOLFSSH_VERSION_STRING "\r\n"; +static const char OpenSSH[] = "SSH-2.0-OpenSSH"; #ifndef WOLFSSH_DEFAULT_GEXDH_MIN #define WOLFSSH_DEFAULT_GEXDH_MIN 1024 #endif @@ -330,7 +331,7 @@ WOLFSSH* SshInit(WOLFSSH* ssh, WOLFSSH_CTX* ctx) WLOG(WS_LOG_DEBUG, "SshInit: Cannot allocate memory.\n"); WFREE(handshake, heap, DYNTYPE_HS); WFREE(rng, heap, DYNTYPE_RNG); - wolfSSH_free(ssh); + WFREE(ssh, heap, DYNTYPE_SSH); return NULL; } @@ -830,6 +831,8 @@ void ChannelDelete(WOLFSSH_CHANNEL* channel, void* heap) if (channel) { WFREE(channel->inputBuffer.buffer, channel->inputBuffer.heap, DYNTYPE_BUFFER); + if (channel->command) + WFREE(channel->command, heap, DYNTYPE_STRING); WFREE(channel, heap, DYNTYPE_CHANNEL); } } @@ -842,7 +845,8 @@ WOLFSSH_CHANNEL* ChannelFind(WOLFSSH* ssh, word32 channel, byte peer) { WOLFSSH_CHANNEL* findChannel = NULL; - WLOG(WS_LOG_DEBUG, "Entering ChannelFind()"); + 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"); @@ -1177,7 +1181,7 @@ static int SendBuffered(WOLFSSH* ssh) while (ssh->outputBuffer.length > 0) { int sent = ssh->ctx->ioSendCb(ssh, ssh->outputBuffer.buffer + ssh->outputBuffer.idx, - ssh->outputBuffer.length, ssh->ioReadCtx); + ssh->outputBuffer.length, ssh->ioWriteCtx); if (sent < 0) { switch (sent) { @@ -1362,6 +1366,33 @@ static int GetString(char* s, word32* sSz, } +/* 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. */ +static int GetStringAlloc(WOLFSSH* ssh, 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 || *idx + strSz > len) + return WS_BUFFER_E; + str = (char*)WMALLOC(strSz + 1, ssh->ctx->heap, DYNTYPE_STRING); + if (str == NULL) + return WS_MEMORY_E; + WMEMCPY(str, buf + *idx, strSz); + *idx += strSz; + str[strSz] = '\0'; + *s = str; + } + + return result; +} + + static int GetNameList(byte* idList, word32* idListSz, byte* buf, word32 len, word32* idx) { @@ -3389,7 +3420,7 @@ static int DoChannelOpen(WOLFSSH* ssh, ssh->defaultPeerChannelId = peerChannelId; ChannelAppend(ssh, newChannel); - ssh->clientState = CLIENT_DONE; + ssh->clientState = CLIENT_CHANNEL_OPEN_DONE; } } @@ -3495,6 +3526,33 @@ static int DoChannelOpenFail(WOLFSSH* ssh, } +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, FIND_SELF); + if (channel == NULL) + ret = WS_INVALID_CHANID; + } + + channel->receivedEof = 1; + + WLOG(WS_LOG_DEBUG, "Leaving DoChannelEof(), ret = %d", ret); + return ret; +} + + static int DoChannelClose(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { @@ -3516,7 +3574,7 @@ static int DoChannelClose(WOLFSSH* ssh, } if (ret == WS_SUCCESS) - ret = SendChannelClose(ssh, channelId); + ret = SendChannelClose(ssh, channel->peerChannel); if (ret == WS_SUCCESS) ret = ChannelRemove(ssh, channelId, FIND_SELF); @@ -3532,6 +3590,7 @@ static int DoChannelClose(WOLFSSH* ssh, static int DoChannelRequest(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { + WOLFSSH_CHANNEL* channel = NULL; word32 begin = *idx; word32 channelId; word32 typeSz; @@ -3555,6 +3614,12 @@ static int DoChannelRequest(WOLFSSH* ssh, return ret; } + if (ret == WS_SUCCESS) { + channel = ChannelFind(ssh, channelId, FIND_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); @@ -3602,6 +3667,24 @@ static int DoChannelRequest(WOLFSSH* ssh, 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, &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, &channel->command, buf, len, &begin); + channel->sessionType = WOLFSSH_SESSION_SUBSYSTEM; + ssh->clientState = CLIENT_DONE; + + WLOG(WS_LOG_DEBUG, " subsystem = %s", channel->command); + } } if (ret == WS_SUCCESS) @@ -3866,6 +3949,7 @@ static int DoPacket(WOLFSSH* ssh) case MSGID_CHANNEL_EOF: WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_EOF"); + ret = DoChannelEof(ssh, buf + idx, payloadSz, &payloadIdx); ret = WS_SUCCESS; break; @@ -4340,6 +4424,10 @@ int DoProtoId(WOLFSSH* ssh) 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; + } *eol = 0; @@ -6044,44 +6132,48 @@ int SendUserAuthBanner(WOLFSSH* ssh) byte* output; word32 idx; int ret = WS_SUCCESS; - const char* banner; + const char* banner = NULL; word32 bannerSz = 0; + WLOG(WS_LOG_DEBUG, "Entering SendUserAuthBanner()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; - if (ssh->ctx->banner != NULL && ssh->ctx->bannerSz > 0) { + if (ret == WS_SUCCESS) { banner = ssh->ctx->banner; bannerSz = ssh->ctx->bannerSz; } - if (ret == WS_SUCCESS) - ret = PreparePacket(ssh, MSG_ID_SZ + (LENGTH_SZ * 2) + - bannerSz + cannedLangTagSz); + 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; + 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; + 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; + ssh->outputBuffer.length = idx; - ret = BundlePacket(ssh); + ret = BundlePacket(ssh); + } } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); + WLOG(WS_LOG_DEBUG, "Leaving SendUserAuthBanner()"); return ret; } @@ -6273,6 +6365,111 @@ int SendChannelEof(WOLFSSH* ssh, word32 peerChannelId) } +int SendChannelEow(WOLFSSH* ssh, word32 peerChannelId) +{ + byte* output; + word32 idx; + word32 strSz = sizeof("eow@openssh.com"); + int ret = WS_SUCCESS; + WOLFSSH_CHANNEL* channel; + + WLOG(WS_LOG_DEBUG, "Entering SendChannelEow()"); + + if (ssh == NULL) + ret = WS_BAD_ARGUMENT; + + if (!ssh->clientOpenSSH) { + WLOG(WS_LOG_DEBUG, "Leaving SendChannelEow(), not OpenSSH"); + return ret; + } + + if (ret == WS_SUCCESS) { + channel = ChannelFind(ssh, peerChannelId, FIND_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 = SendBuffered(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; + + WLOG(WS_LOG_DEBUG, "Entering SendChannelExit(), status = %d", status); + + if (ssh == NULL) + ret = WS_BAD_ARGUMENT; + + if (ret == WS_SUCCESS) { + channel = ChannelFind(ssh, peerChannelId, FIND_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 = SendBuffered(ssh); + + WLOG(WS_LOG_DEBUG, "Leaving SendChannelExit(), ret = %d", ret); + return ret; +} + + int SendChannelClose(WOLFSSH* ssh, word32 peerChannelId) { byte* output; @@ -6289,6 +6486,10 @@ int SendChannelClose(WOLFSSH* ssh, word32 peerChannelId) channel = ChannelFind(ssh, peerChannelId, FIND_PEER); if (channel == NULL) ret = WS_INVALID_CHANID; + else if (channel->closeSent) { + WLOG(WS_LOG_DEBUG, "Leaving SendChannelClose(), already sent"); + return ret; + } } if (ret == WS_SUCCESS) @@ -6307,8 +6508,10 @@ int SendChannelClose(WOLFSSH* ssh, word32 peerChannelId) ret = BundlePacket(ssh); } - if (ret == WS_SUCCESS) + if (ret == WS_SUCCESS) { ret = SendBuffered(ssh); + channel->closeSent = 1; + } WLOG(WS_LOG_DEBUG, "Leaving SendChannelClose(), ret = %d", ret); return ret; diff --git a/src/ssh.c b/src/ssh.c index 07a5d1b..af8bd30 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -346,7 +346,7 @@ int wolfSSH_accept(WOLFSSH* ssh) FALL_THROUGH; case ACCEPT_SERVER_USERAUTH_SENT: - while (ssh->clientState < CLIENT_DONE) { + while (ssh->clientState < CLIENT_CHANNEL_OPEN_DONE) { if ( (ssh->error = DoReceive(ssh)) < 0) { WLOG(WS_LOG_DEBUG, acceptError, "SERVER_USERAUTH_SENT", ssh->error); @@ -365,6 +365,18 @@ int wolfSSH_accept(WOLFSSH* ssh) } ssh->acceptState = ACCEPT_SERVER_CHANNEL_ACCEPT_SENT; WLOG(WS_LOG_DEBUG, acceptState, "SERVER_CHANNEL_ACCEPT_SENT"); + + case ACCEPT_SERVER_CHANNEL_ACCEPT_SENT: + while (ssh->clientState < CLIENT_DONE) { + if ( (ssh->error = DoReceive(ssh)) < 0) { + WLOG(WS_LOG_DEBUG, acceptError, + "SERVER_CHANNEL_ACCEPT_SENT", ssh->error); + return WS_FATAL_ERROR; + } + } + ssh->acceptState = ACCEPT_CLIENT_SESSION_ESTABLISHED; + WLOG(WS_LOG_DEBUG, acceptState, "CLIENT_SESSION_ESTABLISHED"); + } return WS_SUCCESS; @@ -604,6 +616,8 @@ int wolfSSH_stream_read(WOLFSSH* ssh, byte* buf, word32 bufSz) while (inputBuffer->length - inputBuffer->idx == 0) { int ret = DoReceive(ssh); + if (ssh->channelList == NULL || ssh->channelList->receivedEof) + ret = WS_EOF; if (ret < 0) { WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_stream_read(), ret = %d", ret); return ret; @@ -663,6 +677,32 @@ int wolfSSH_stream_send(WOLFSSH* ssh, byte* buf, word32 bufSz) } +int wolfSSH_stream_exit(WOLFSSH* ssh, int status) +{ + int ret = WS_SUCCESS; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_stream_exit(), status = %d", status); + + if (ssh == NULL || ssh->channelList == NULL) + ret = WS_BAD_ARGUMENT; + + if (ret == WS_SUCCESS) + ret = SendChannelExit(ssh, ssh->channelList->peerChannel, status); + + if (ret == WS_SUCCESS) + ret = SendChannelEow(ssh, ssh->channelList->peerChannel); + + if (ret == WS_SUCCESS) + ret = SendChannelEof(ssh, ssh->channelList->peerChannel); + + if (ret == WS_SUCCESS) + ret = SendChannelClose(ssh, ssh->channelList->peerChannel); + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_stream_exit()"); + return ret; +} + + void wolfSSH_SetUserAuth(WOLFSSH_CTX* ctx, WS_CallbackUserAuth cb) { if (ctx != NULL) { @@ -790,3 +830,24 @@ int wolfSSH_KDF(byte hashId, byte keyId, sessionId, sessionIdSz); } + +WS_SessionType wolfSSH_GetSessionType(const WOLFSSH* ssh) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_GetSessionCommand()"); + + if (ssh && ssh->channelList) + return ssh->channelList->sessionType; + + return WOLFSSH_SESSION_UNKNOWN; +} + + +const char* wolfSSH_GetSessionCommand(const WOLFSSH* ssh) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_GetSessionCommand()"); + + if (ssh && ssh->channelList) + return ssh->channelList->command; + + return NULL; +} diff --git a/wolfssh/error.h b/wolfssh/error.h index 87b569e..b405350 100644 --- a/wolfssh/error.h +++ b/wolfssh/error.h @@ -68,6 +68,7 @@ enum WS_ErrorCodes { WS_INVALID_USERNAME = -28, WS_CRYPTO_FAILED = -29, /* crypto action failed */ WS_INVALID_STATE_E = -30, + WS_EOF = -31, WS_INVALID_PRIME_CURVE = -32, WS_ECC_E = -33, WS_CHANOPEN_FAILED = -34, diff --git a/wolfssh/internal.h b/wolfssh/internal.h index 7513a91..9f790d7 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -258,6 +258,7 @@ struct WOLFSSH { byte connReset; byte isClosed; + byte clientOpenSSH; byte blockSz; byte encryptId; @@ -307,6 +308,9 @@ struct WOLFSSH { struct WOLFSSH_CHANNEL { byte channelType; + byte sessionType; + byte closeSent; + byte receivedEof; word32 channel; word32 windowSz; word32 maxPacketSz; @@ -314,6 +318,7 @@ struct WOLFSSH_CHANNEL { word32 peerWindowSz; word32 peerMaxPacketSz; Buffer inputBuffer; + char* command; struct WOLFSSH* ssh; struct WOLFSSH_CHANNEL* next; }; @@ -369,7 +374,9 @@ WOLFSSH_LOCAL int SendRequestSuccess(WOLFSSH*, int); WOLFSSH_LOCAL int SendChannelOpenSession(WOLFSSH*, word32, word32); WOLFSSH_LOCAL int SendChannelOpenConf(WOLFSSH*); WOLFSSH_LOCAL int SendChannelEof(WOLFSSH*, word32); +WOLFSSH_LOCAL int SendChannelEow(WOLFSSH*, word32); WOLFSSH_LOCAL int SendChannelClose(WOLFSSH*, word32); +WOLFSSH_LOCAL int SendChannelExit(WOLFSSH*, word32, int); WOLFSSH_LOCAL int SendChannelData(WOLFSSH*, word32, byte*, word32); WOLFSSH_LOCAL int SendChannelWindowAdjust(WOLFSSH*, word32, word32); WOLFSSH_LOCAL int SendChannelRequestShell(WOLFSSH*); @@ -389,7 +396,8 @@ enum AcceptStates { ACCEPT_CLIENT_USERAUTH_DONE, ACCEPT_SERVER_USERAUTH_SENT, ACCEPT_CLIENT_CHANNEL_REQUEST_DONE, - ACCEPT_SERVER_CHANNEL_ACCEPT_SENT + ACCEPT_SERVER_CHANNEL_ACCEPT_SENT, + ACCEPT_CLIENT_SESSION_ESTABLISHED }; @@ -419,6 +427,7 @@ enum ClientStates { CLIENT_KEXDH_INIT_DONE, CLIENT_USERAUTH_REQUEST_DONE, CLIENT_USERAUTH_DONE, + CLIENT_CHANNEL_OPEN_DONE, CLIENT_DONE }; diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h index 84092c5..9b5fdda 100644 --- a/wolfssh/ssh.h +++ b/wolfssh/ssh.h @@ -136,6 +136,7 @@ WOLFSSH_API int wolfSSH_connect(WOLFSSH*); WOLFSSH_API int wolfSSH_shutdown(WOLFSSH*); WOLFSSH_API int wolfSSH_stream_read(WOLFSSH*, byte*, word32); WOLFSSH_API int wolfSSH_stream_send(WOLFSSH*, byte*, word32); +WOLFSSH_API int wolfSSH_stream_exit(WOLFSSH*, int); WOLFSSH_API int wolfSSH_TriggerKeyExchange(WOLFSSH*); WOLFSSH_API void wolfSSH_GetStats(WOLFSSH*, @@ -145,6 +146,17 @@ WOLFSSH_API int wolfSSH_KDF(byte, byte, byte*, word32, const byte*, word32, const byte*, word32, const byte*, word32); +typedef enum { + WOLFSSH_SESSION_UNKNOWN = 0, + WOLFSSH_SESSION_SHELL, + WOLFSSH_SESSION_EXEC, + WOLFSSH_SESSION_SUBSYSTEM, +} WS_SessionType; + +WOLFSSH_API WS_SessionType wolfSSH_GetSessionType(const WOLFSSH*); +WOLFSSH_API const char* wolfSSH_GetSessionCommand(const WOLFSSH*); + + enum WS_HighwaterSide { WOLFSSH_HWSIDE_TRANSMIT, WOLFSSH_HWSIDE_RECEIVE