/* ssh.c * * Copyright (C) 2014-2023 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 . */ /* * The ssh module contains the public API for wolfSSH. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #ifdef NO_INLINE #include #else #define WOLFSSH_MISC_INCLUDED #include "src/misc.c" #endif #ifdef HAVE_FIPS #include static void myFipsCb(int ok, int err, const char* hash) { printf("in my Fips callback, ok = %d, err = %d\n", ok, err); printf("message = %s\n", wc_GetErrorString(err)); printf("hash = %s\n", hash); if (err == IN_CORE_FIPS_E) { printf("In core integrity hash check failure, copy above hash\n"); printf("into verifyCore[] in fips_test.c and rebuild\n"); } } #endif /* HAVE_FIPS */ int wolfSSH_Init(void) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_Init()"); if (wolfCrypt_Init() != 0) ret = WS_CRYPTO_FAILED; #ifdef HAVE_FIPS wolfCrypt_SetCb_fips(myFipsCb); #endif #ifdef WC_RNG_SEED_CB wc_SetSeed_Cb(wc_GenerateSeed); #endif #if defined(WOLFSSH_ZEPHYR) && (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP)) if (wssh_z_fds_init() != 0) ret = WS_CRYPTO_FAILED; #endif WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_Init(), returning %d", ret); return ret; } int wolfSSH_Cleanup(void) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_Cleanup()"); if (wolfCrypt_Cleanup() != 0) ret = WS_CRYPTO_FAILED; #if defined(WOLFSSH_ZEPHYR) && (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP)) if (wssh_z_fds_cleanup() != 0) ret = WS_CRYPTO_FAILED; #endif WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_Cleanup(), returning %d", ret); return ret; } WOLFSSH_CTX* wolfSSH_CTX_new(byte side, void* heap) { WOLFSSH_CTX* ctx; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_new()"); if (side != WOLFSSH_ENDPOINT_SERVER && side != WOLFSSH_ENDPOINT_CLIENT) { WLOG(WS_LOG_DEBUG, "Invalid endpoint type"); return NULL; } ctx = (WOLFSSH_CTX*)WMALLOC(sizeof(WOLFSSH_CTX), heap, DYNTYPE_CTX); if (CtxInit(ctx, side, heap) == NULL) { WFREE(ctx, heap, DYNTYPE_CTX); ctx = NULL; } WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_CTX_new(), ctx = %p", ctx); return ctx; } void wolfSSH_CTX_free(WOLFSSH_CTX* ctx) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_free()"); if (ctx) { CtxResourceFree(ctx); WFREE(ctx, ctx->heap, DYNTYPE_CTX); } } WOLFSSH* wolfSSH_new(WOLFSSH_CTX* ctx) { WOLFSSH* ssh; void* heap = NULL; WOLFSSH_UNUSED(heap); WLOG(WS_LOG_DEBUG, "Entering wolfSSH_new()"); if (ctx) heap = ctx->heap; else { WLOG(WS_LOG_ERROR, "Trying to init a wolfSSH w/o wolfSSH_CTX"); return NULL; } ssh = (WOLFSSH*)WMALLOC(sizeof(WOLFSSH), heap, DYNTYPE_SSH); ssh = SshInit(ssh, ctx); WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_new(), ssh = %p", ssh); return ssh; } void wolfSSH_free(WOLFSSH* ssh) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_free()"); if (ssh) { void* heap = ssh->ctx ? ssh->ctx->heap : NULL; #ifdef WOLFSSH_SFTP if (wolfSSH_SFTP_free(ssh) != WS_SUCCESS) { WLOG(WS_LOG_SFTP, "Error cleaning up SFTP connection"); } #endif #ifdef WOLFSSH_AGENT if (ssh->agent != NULL) wolfSSH_AGENT_free(ssh->agent); #endif /* WOLFSSH_AGENT */ SshResourceFree(ssh, heap); WFREE(ssh, heap, DYNTYPE_SSH); } } int wolfSSH_set_fd(WOLFSSH* ssh, WS_SOCKET_T fd) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_set_fd()"); if (ssh) { ssh->rfd = fd; ssh->wfd = fd; ssh->ioReadCtx = &ssh->rfd; ssh->ioWriteCtx = &ssh->wfd; return WS_SUCCESS; } return WS_BAD_ARGUMENT; } WS_SOCKET_T wolfSSH_get_fd(const WOLFSSH* ssh) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_get_fd()"); if (ssh) return ssh->rfd; #ifdef USE_WINDOWS_API return INVALID_SOCKET; #else return WS_BAD_ARGUMENT; #endif } int wolfSSH_SetFilesystemHandle(WOLFSSH* ssh, void* handle) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetFilesystemHandle()"); if (ssh) { ssh->fs = handle; return WS_SUCCESS; } return WS_BAD_ARGUMENT; } void* wolfSSH_GetFilesystemHandle(WOLFSSH* ssh) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_GetFilesystemHandle()"); if (ssh) return ssh->fs; return NULL; } int wolfSSH_SetHighwater(WOLFSSH* ssh, word32 highwater) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetHighwater()"); if (ssh) { ssh->highwaterMark = highwater; return WS_SUCCESS; } return WS_BAD_ARGUMENT; } word32 wolfSSH_GetHighwater(WOLFSSH* ssh) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_GetHighwater()"); if (ssh) return ssh->highwaterMark; return 0; } void wolfSSH_SetHighwaterCb(WOLFSSH_CTX* ctx, word32 highwater, WS_CallbackHighwater cb) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetHighwaterCb()"); if (ctx) { ctx->highwaterMark = highwater; ctx->highwaterCb = cb; } } void wolfSSH_SetHighwaterCtx(WOLFSSH* ssh, void* ctx) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetHighwaterCtx()"); if (ssh) ssh->highwaterCtx = ctx; } void* wolfSSH_GetHighwaterCtx(WOLFSSH* ssh) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_GetHighwaterCtx()"); if (ssh) return ssh->highwaterCtx; return NULL; } void wolfSSH_SetGlobalReq(WOLFSSH_CTX *ctx, WS_CallbackGlobalReq cb) { if (ctx) ctx->globalReqCb = cb; } void wolfSSH_SetReqSuccess(WOLFSSH_CTX *ctx, WS_CallbackReqSuccess cb) { if (ctx) ctx->reqSuccessCb = cb; } void wolfSSH_SetReqFailure(WOLFSSH_CTX *ctx, WS_CallbackReqSuccess cb) { if (ctx) ctx->reqFailureCb = cb; } void wolfSSH_SetGlobalReqCtx(WOLFSSH* ssh, void *ctx) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetGlobalReqCtx()"); if (ssh) ssh->globalReqCtx = ctx; } void *wolfSSH_GetGlobalReqCtx(WOLFSSH* ssh) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_GetGlobalReqCtx()"); if (ssh) return ssh->globalReqCtx; return NULL; } void wolfSSH_SetReqSuccessCtx(WOLFSSH *ssh, void *ctx) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetReqSuccessCtx()"); if (ssh) ssh->reqSuccessCtx = ctx; } void *wolfSSH_GetReqSuccessCtx(WOLFSSH *ssh) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_GetReqSuccessCtx()"); if (ssh) return ssh->reqSuccessCtx; return NULL; } void wolfSSH_SetReqFailureCtx(WOLFSSH *ssh, void *ctx) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetReqFailureCtx()"); if (ssh) ssh->reqFailureCtx = ctx; } void *wolfSSH_GetReqFailureCtx(WOLFSSH *ssh) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_GetReqFailureCtx()"); if (ssh) return ssh->reqFailureCtx; return NULL; } int wolfSSH_get_error(const WOLFSSH* ssh) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_get_error()"); if (ssh) return ssh->error; return WS_SSH_NULL_E; } const char* wolfSSH_get_error_name(const WOLFSSH* ssh) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_get_error_name()"); if (ssh) return GetErrorString(ssh->error); return GetErrorString(WS_SSH_NULL_E); } const char* wolfSSH_ErrorToName(int err) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ErrorToName()"); return GetErrorString(err); } #ifndef NO_WOLFSSH_SERVER const char acceptError[] = "accept error: %s, %d"; const char acceptState[] = "accept state: %s"; int wolfSSH_accept(WOLFSSH* ssh) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_accept()"); if (ssh == NULL) return WS_BAD_ARGUMENT; /* clear want read/writes for retry */ if (ssh->error == WS_WANT_READ || ssh->error == WS_WANT_WRITE) ssh->error = 0; if (ssh->error != 0) { WLOG(WS_LOG_DEBUG, "Calling wolfSSH_accept in error state"); return WS_INVALID_STATE_E; } /* check if data pending to be sent */ if (ssh->outputBuffer.length > 0 && ssh->acceptState < ACCEPT_CLIENT_SESSION_ESTABLISHED) { if ((ssh->error = wolfSSH_SendPacket(ssh)) == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "Sent pending packet"); /* adjust state, a couple of them use multiple sends */ if (ssh->acceptState != ACCEPT_SERVER_VERSION_SENT && ssh->acceptState != ACCEPT_SERVER_USERAUTH_ACCEPT_SENT && ssh->acceptState != ACCEPT_SERVER_KEXINIT_SENT && ssh->acceptState != ACCEPT_KEYED && ssh->acceptState != ACCEPT_SERVER_CHANNEL_ACCEPT_SENT) { WLOG(WS_LOG_DEBUG, "Advancing accept state"); ssh->acceptState++; } /* handle in process reply state */ if (ssh->processReplyState == PROCESS_PACKET) { WLOG(WS_LOG_DEBUG, "PR3: peerMacSz = %u", ssh->peerMacSz); ssh->inputBuffer.idx += ssh->peerMacSz; 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); } } else { return WS_FATAL_ERROR; } } while (ssh->acceptState != ACCEPT_CLIENT_SESSION_ESTABLISHED) { switch (ssh->acceptState) { case ACCEPT_BEGIN: if ( (ssh->error = SendProtoId(ssh)) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, acceptError, "BEGIN", ssh->error); return WS_FATAL_ERROR; } ssh->acceptState = ACCEPT_SERVER_VERSION_SENT; WLOG(WS_LOG_DEBUG, acceptState, "SERVER_VERSION_SENT"); NO_BREAK; case ACCEPT_SERVER_VERSION_SENT: while (ssh->clientState < CLIENT_VERSION_DONE) { if ( (ssh->error = DoProtoId(ssh)) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, acceptError, "SERVER_VERSION_SENT", ssh->error); return WS_FATAL_ERROR; } } ssh->acceptState = ACCEPT_CLIENT_VERSION_DONE; WLOG(WS_LOG_DEBUG, acceptState, "CLIENT_VERSION_DONE"); NO_BREAK; case ACCEPT_CLIENT_VERSION_DONE: if ( (ssh->error = SendKexInit(ssh)) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, acceptError, "CLIENT_VERSION_DONE", ssh->error); return WS_FATAL_ERROR; } ssh->acceptState = ACCEPT_SERVER_KEXINIT_SENT; WLOG(WS_LOG_DEBUG, acceptState, "SERVER_KEXINIT_SENT"); NO_BREAK; case ACCEPT_SERVER_KEXINIT_SENT: while (ssh->isKeying) { if (DoReceive(ssh) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, acceptError, "SERVER_KEXINIT_SENT", ssh->error); return WS_FATAL_ERROR; } } ssh->acceptState = ACCEPT_KEYED; WLOG(WS_LOG_DEBUG, acceptState, "KEYED"); NO_BREAK; case ACCEPT_KEYED: while (ssh->clientState < CLIENT_USERAUTH_REQUEST_DONE) { if (DoReceive(ssh) < 0) { WLOG(WS_LOG_DEBUG, acceptError, "KEYED", ssh->error); return WS_FATAL_ERROR; } } ssh->acceptState = ACCEPT_CLIENT_USERAUTH_REQUEST_DONE; WLOG(WS_LOG_DEBUG, acceptState, "CLIENT_USERAUTH_REQUEST_DONE"); NO_BREAK; case ACCEPT_CLIENT_USERAUTH_REQUEST_DONE: if ( (ssh->error = SendServiceAccept(ssh, ID_SERVICE_USERAUTH)) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, acceptError, "CLIENT_USERAUTH_REQUEST_DONE", ssh->error); return WS_FATAL_ERROR; } ssh->acceptState = ACCEPT_SERVER_USERAUTH_ACCEPT_SENT; WLOG(WS_LOG_DEBUG, acceptState, "ACCEPT_SERVER_USERAUTH_ACCEPT_SENT"); NO_BREAK; case ACCEPT_SERVER_USERAUTH_ACCEPT_SENT: while (ssh->clientState < CLIENT_USERAUTH_DONE) { if (DoReceive(ssh) < 0) { WLOG(WS_LOG_DEBUG, acceptError, "SERVER_USERAUTH_ACCEPT_SENT", ssh->error); return WS_FATAL_ERROR; } } ssh->acceptState = ACCEPT_CLIENT_USERAUTH_DONE; WLOG(WS_LOG_DEBUG, acceptState, "CLIENT_USERAUTH_DONE"); NO_BREAK; case ACCEPT_CLIENT_USERAUTH_DONE: if ( (ssh->error = SendUserAuthSuccess(ssh)) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, acceptError, "CLIENT_USERAUTH_DONE", ssh->error); return WS_FATAL_ERROR; } ssh->acceptState = ACCEPT_SERVER_USERAUTH_SENT; WLOG(WS_LOG_DEBUG, acceptState, "SERVER_USERAUTH_SENT"); NO_BREAK; case ACCEPT_SERVER_USERAUTH_SENT: while (ssh->clientState < CLIENT_CHANNEL_OPEN_DONE) { if (DoReceive(ssh) < 0) { WLOG(WS_LOG_DEBUG, acceptError, "SERVER_USERAUTH_SENT", ssh->error); return WS_FATAL_ERROR; } } ssh->acceptState = ACCEPT_SERVER_CHANNEL_ACCEPT_SENT; WLOG(WS_LOG_DEBUG, acceptState, "SERVER_CHANNEL_ACCEPT_SENT"); NO_BREAK; case ACCEPT_SERVER_CHANNEL_ACCEPT_SENT: while (ssh->clientState < CLIENT_DONE) { if (DoReceive(ssh) < 0) { WLOG(WS_LOG_DEBUG, acceptError, "SERVER_CHANNEL_ACCEPT_SENT", ssh->error); return WS_FATAL_ERROR; } } #ifdef WOLFSSH_SCP if (ChannelCommandIsScp(ssh)) { ssh->acceptState = ACCEPT_INIT_SCP_TRANSFER; WLOG(WS_LOG_DEBUG, acceptState, "ACCEPT_INIT_SCP_TRANSFER"); return WS_SCP_INIT; } #endif #if defined(WOLFSSH_SFTP) && !defined(NO_WOLFSSH_SERVER) { const char* cmd = wolfSSH_GetSessionCommand(ssh); if (cmd != NULL && WOLFSSH_SESSION_SUBSYSTEM == wolfSSH_GetSessionType(ssh) && (WSTRNCMP(cmd, "sftp", 4) == 0)) { ssh->acceptState = ACCEPT_INIT_SFTP; return wolfSSH_SFTP_accept(ssh); } } #endif /* WOLFSSH_SFTP and !NO_WOLFSSH_SERVER */ #ifdef WOLFSSH_AGENT if (ssh->useAgent) { WOLFSSH_AGENT_CTX* newAgent; WOLFSSH_CHANNEL* newChannel; WLOG(WS_LOG_AGENT, "Starting agent channel"); newAgent = wolfSSH_AGENT_new(ssh->ctx->heap); if (newAgent == NULL) { ssh->error = WS_MEMORY_E; WLOG(WS_LOG_DEBUG, acceptError, "SERVER_USERAUTH_ACCEPT_DONE", ssh->error); return WS_ERROR; } newChannel = ChannelNew(ssh, ID_CHANTYPE_AUTH_AGENT, ssh->ctx->windowSz, ssh->ctx->maxPacketSz); if (newChannel == NULL) { wolfSSH_AGENT_free(newAgent); ssh->error = WS_MEMORY_E; WLOG(WS_LOG_DEBUG, acceptError, "SERVER_USERAUTH_ACCEPT_DONE", ssh->error); return WS_FATAL_ERROR; } ssh->error = SendChannelOpenSession(ssh, newChannel); if (ssh->error < WS_SUCCESS) { if (ssh->error == WS_WANT_WRITE || ssh->error == WS_WANT_READ) { ChannelAppend(ssh, newChannel); } else { ChannelDelete(newChannel, ssh->ctx->heap); wolfSSH_AGENT_free(newAgent); } WLOG(WS_LOG_DEBUG, acceptError, "SERVER_USERAUTH_ACCEPT_DONE", ssh->error); return WS_FATAL_ERROR; } ChannelAppend(ssh, newChannel); newAgent->channel = newChannel->channel; if (ssh->ctx->agentCb) { ssh->ctx->agentCb(WOLFSSH_AGENT_LOCAL_SETUP, ssh->agentCbCtx); } if (ssh->agent != NULL) wolfSSH_AGENT_free(ssh->agent); ssh->agent = newAgent; } #endif /* WOLFSSH_AGENT */ ssh->acceptState = ACCEPT_CLIENT_SESSION_ESTABLISHED; WLOG(WS_LOG_DEBUG, acceptState, "CLIENT_SESSION_ESTABLISHED"); break; #ifdef WOLFSSH_SCP case ACCEPT_INIT_SCP_TRANSFER: if (DoScpRequest(ssh) < 0) { WLOG(WS_LOG_DEBUG, acceptError, "INIT_SCP_TRANSFER", ssh->error); return WS_FATAL_ERROR; } return WS_SCP_COMPLETE; #endif #ifdef WOLFSSH_SFTP case ACCEPT_INIT_SFTP: return wolfSSH_SFTP_accept(ssh); #endif } } /* end while */ return WS_SUCCESS; } #endif /* NO_WOLFSSH_SERVER */ #ifndef NO_WOLFSSH_CLIENT const char connectError[] = "connect error: %s, %d"; const char connectState[] = "connect state: %s"; int wolfSSH_connect(WOLFSSH* ssh) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_connect()"); if (ssh == NULL) return WS_BAD_ARGUMENT; /* check if data pending to be sent */ if (ssh->outputBuffer.length > 0 && ssh->connectState < CONNECT_SERVER_CHANNEL_REQUEST_DONE) { if ((ssh->error = wolfSSH_SendPacket(ssh)) == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "Sent pending packet"); /* adjust state, a couple of them use multiple sends */ if (ssh->connectState != CONNECT_CLIENT_VERSION_SENT && ssh->connectState != CONNECT_CLIENT_KEXINIT_SENT && ssh->connectState != CONNECT_CLIENT_KEXDH_INIT_SENT && ssh->connectState != CONNECT_CLIENT_USERAUTH_REQUEST_SENT && ssh->connectState != CONNECT_CLIENT_USERAUTH_SENT && ssh->connectState != CONNECT_CLIENT_CHANNEL_OPEN_SESSION_SENT && ssh->connectState != CONNECT_CLIENT_CHANNEL_REQUEST_SENT) { WLOG(WS_LOG_DEBUG, "Advancing connect state"); ssh->connectState++; } /* handle in process reply state */ if (ssh->processReplyState == PROCESS_PACKET) { WLOG(WS_LOG_DEBUG, "PR3: peerMacSz = %u", ssh->peerMacSz); ssh->inputBuffer.idx += ssh->peerMacSz; 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); } } else { return WS_FATAL_ERROR; } } switch (ssh->connectState) { case CONNECT_BEGIN: if ( (ssh->error = SendProtoId(ssh)) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, connectError, "BEGIN", ssh->error); return WS_FATAL_ERROR; } ssh->connectState = CONNECT_CLIENT_VERSION_SENT; WLOG(WS_LOG_DEBUG, connectState, "CLIENT_VERSION_SENT"); NO_BREAK; case CONNECT_CLIENT_VERSION_SENT: while (ssh->serverState < SERVER_VERSION_DONE) { if ( (ssh->error = DoProtoId(ssh)) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, connectError, "CLIENT_VERSION_SENT", ssh->error); return WS_FATAL_ERROR; } } ssh->connectState = CONNECT_SERVER_VERSION_DONE; WLOG(WS_LOG_DEBUG, connectState, "SERVER_VERSION_DONE"); NO_BREAK; case CONNECT_SERVER_VERSION_DONE: if ( (ssh->error = SendKexInit(ssh)) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, connectError, "SERVER_VERSION_DONE", ssh->error); return WS_FATAL_ERROR; } ssh->connectState = CONNECT_CLIENT_KEXINIT_SENT; WLOG(WS_LOG_DEBUG, connectState, "CLIENT_KEXINIT_SENT"); NO_BREAK; case CONNECT_CLIENT_KEXINIT_SENT: while (ssh->serverState < SERVER_KEXINIT_DONE) { if (DoReceive(ssh) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, connectError, "CLIENT_KEXINIT_SENT", ssh->error); return WS_FATAL_ERROR; } } ssh->connectState = CONNECT_SERVER_KEXINIT_DONE; WLOG(WS_LOG_DEBUG, connectState, "SERVER_KEXINIT_DONE"); NO_BREAK; case CONNECT_SERVER_KEXINIT_DONE: if (ssh->handshake == NULL) { return WS_FATAL_ERROR; } 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); if (ssh->error < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, connectError, "SERVER_KEXINIT_DONE", ssh->error); return WS_FATAL_ERROR; } ssh->connectState = CONNECT_CLIENT_KEXDH_INIT_SENT; WLOG(WS_LOG_DEBUG, connectState, "CLIENT_KEXDH_INIT_SENT"); NO_BREAK; case CONNECT_CLIENT_KEXDH_INIT_SENT: while (ssh->isKeying) { if (DoReceive(ssh) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, connectError, "CLIENT_KEXDH_INIT_SENT", ssh->error); return WS_FATAL_ERROR; } } ssh->connectState = CONNECT_KEYED; WLOG(WS_LOG_DEBUG, connectState, "KEYED"); NO_BREAK; case CONNECT_KEYED: if ( (ssh->error = SendServiceRequest(ssh, ID_SERVICE_USERAUTH)) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, connectError, "KEYED", ssh->error); return WS_FATAL_ERROR; } ssh->connectState = CONNECT_CLIENT_USERAUTH_REQUEST_SENT; WLOG(WS_LOG_DEBUG, connectState, "CLIENT_USERAUTH_REQUEST_SENT"); NO_BREAK; case CONNECT_CLIENT_USERAUTH_REQUEST_SENT: while (ssh->serverState < SERVER_USERAUTH_REQUEST_DONE) { if (DoReceive(ssh) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, connectError, "CLIENT_USERAUTH_REQUEST_SENT", ssh->error); return WS_FATAL_ERROR; } } ssh->connectState = CONNECT_SERVER_USERAUTH_REQUEST_DONE; WLOG(WS_LOG_DEBUG, connectState, "SERVER_USERAUTH_REQUEST_DONE"); NO_BREAK; case CONNECT_SERVER_USERAUTH_REQUEST_DONE: #ifdef WOLFSSH_AGENT if (ssh->agentEnabled) { ssh->agent = wolfSSH_AGENT_new(ssh->ctx->heap); if (ssh->agent == NULL) { ssh->agentEnabled = 0; WLOG(WS_LOG_INFO, "Unable to create agent. Disabling."); } } #endif if ( (ssh->error = SendUserAuthRequest(ssh, ID_NONE, 0)) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, connectError, "SERVER_USERAUTH_REQUEST_DONE", ssh->error); return WS_FATAL_ERROR; } ssh->connectState = CONNECT_CLIENT_USERAUTH_SENT; WLOG(WS_LOG_DEBUG, connectState, "CLIENT_USERAUTH_SENT"); NO_BREAK; case CONNECT_CLIENT_USERAUTH_SENT: while (ssh->serverState < SERVER_USERAUTH_ACCEPT_DONE) { if (DoReceive(ssh) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, connectError, "CLIENT_USERAUTH_SENT", ssh->error); return WS_FATAL_ERROR; } } ssh->connectState = CONNECT_SERVER_USERAUTH_ACCEPT_DONE; WLOG(WS_LOG_DEBUG, connectState, "SERVER_USERAUTH_ACCEPT_DONE"); NO_BREAK; case CONNECT_SERVER_USERAUTH_ACCEPT_DONE: { WOLFSSH_CHANNEL* newChannel; newChannel = ChannelNew(ssh, ID_CHANTYPE_SESSION, ssh->ctx->windowSz, ssh->ctx->maxPacketSz); if (newChannel == NULL) { ssh->error = WS_MEMORY_E; WLOG(WS_LOG_DEBUG, connectError, "SERVER_USERAUTH_ACCEPT_DONE", ssh->error); return WS_FATAL_ERROR; } if ( (ssh->error = SendChannelOpenSession(ssh, newChannel)) < WS_SUCCESS) { if (ssh->error == WS_WANT_WRITE || ssh->error == WS_WANT_READ) { ChannelAppend(ssh, newChannel); } else { ChannelDelete(newChannel, ssh->ctx->heap); } WLOG(WS_LOG_DEBUG, connectError, "SERVER_USERAUTH_ACCEPT_DONE", ssh->error); return WS_FATAL_ERROR; } ChannelAppend(ssh, newChannel); } ssh->connectState = CONNECT_CLIENT_CHANNEL_OPEN_SESSION_SENT; WLOG(WS_LOG_DEBUG, connectState, "CLIENT_CHANNEL_OPEN_SESSION_SENT"); NO_BREAK; case CONNECT_CLIENT_CHANNEL_OPEN_SESSION_SENT: while (ssh->serverState < SERVER_CHANNEL_OPEN_DONE) { if (DoReceive(ssh) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, connectError, "CLIENT_CHANNEL_OPEN_SESSION_SENT", ssh->error); return WS_FATAL_ERROR; } } ssh->connectState = CONNECT_SERVER_CHANNEL_OPEN_SESSION_DONE; WLOG(WS_LOG_DEBUG, connectState, "SERVER_CHANNEL_OPEN_SESSION_DONE"); NO_BREAK; case CONNECT_SERVER_CHANNEL_OPEN_SESSION_DONE: #ifdef WOLFSSH_AGENT if (ssh->agentEnabled) { if ( (ssh->error = SendChannelAgentRequest(ssh)) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, connectError, "SERVER_CHANNEL_OPEN_SESSION_DONE", ssh->error); return WS_FATAL_ERROR; } } #endif WLOG(WS_LOG_DEBUG, connectState, "CLIENT_CHANNEL_AGENT_REQUEST_SENT"); ssh->connectState = CONNECT_CLIENT_CHANNEL_AGENT_REQUEST_SENT; NO_BREAK; case CONNECT_CLIENT_CHANNEL_AGENT_REQUEST_SENT: #if defined(WOLFSSH_TERM) && !defined(NO_FILESYSTEM) if (ssh->sendTerminalRequest) { if ( (ssh->error = SendChannelTerminalRequest(ssh)) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, connectError, "CLIENT_CHANNEL_AGENT_REQUEST_SENT", ssh->error); return WS_FATAL_ERROR; } } #endif WLOG(WS_LOG_DEBUG, connectState, "CLIENT_CHANNEL_TERMINAL_REQUEST_SENT"); ssh->connectState = CONNECT_CLIENT_CHANNEL_TERMINAL_REQUEST_SENT; NO_BREAK; case CONNECT_CLIENT_CHANNEL_TERMINAL_REQUEST_SENT: if ( (ssh->error = SendChannelRequest(ssh, ssh->channelName, ssh->channelNameSz)) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, connectError, "SERVER_CHANNEL_OPEN_SESSION_DONE", ssh->error); return WS_FATAL_ERROR; } ssh->connectState = CONNECT_CLIENT_CHANNEL_REQUEST_SENT; WLOG(WS_LOG_DEBUG, connectState, "CLIENT_CHANNEL_REQUEST_SENT"); NO_BREAK; case CONNECT_CLIENT_CHANNEL_REQUEST_SENT: while (ssh->serverState < SERVER_DONE) { if (DoReceive(ssh) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, connectError, "CLIENT_CHANNEL_REQUEST_SENT", ssh->error); return WS_FATAL_ERROR; } } ssh->connectState = CONNECT_SERVER_CHANNEL_REQUEST_DONE; WLOG(WS_LOG_DEBUG, connectState, "SERVER_CHANNEL_REQUEST_DONE"); } WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_connect()"); return WS_SUCCESS; } #endif /* NO_WOLFSSH_CLIENT */ int wolfSSH_shutdown(WOLFSSH* ssh) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_shutdown()"); if (ssh == NULL || ssh->channelList == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) ret = SendChannelEof(ssh, ssh->channelList->peerChannel); /* continue on success and in case where queing up send packets */ if (ret == WS_SUCCESS || (ret != WS_BAD_ARGUMENT && ssh->error == WS_WANT_WRITE)) ret = SendChannelExit(ssh, ssh->channelList->peerChannel, 0); /* continue on success and in case where queing up send packets */ if (ret == WS_SUCCESS || (ret != WS_BAD_ARGUMENT && ssh->error == WS_WANT_WRITE)) ret = SendChannelClose(ssh, ssh->channelList->peerChannel); if (ssh != NULL && ssh->channelList == NULL) { WLOG(WS_LOG_DEBUG, "channel list was already removed"); ret = WS_SUCCESS; } WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_shutdown(), ret = %d", ret); return ret; } int wolfSSH_TriggerKeyExchange(WOLFSSH* ssh) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_TriggerKeyExchange()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) ret = ssh->error = SendKexInit(ssh); WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_TriggerKeyExchange(), ret = %d", ret); return ret; } /* gets current input buffer if any without advancing the internal index. * returns number of bytes was able to peek at on success */ int wolfSSH_stream_peek(WOLFSSH* ssh, byte* buf, word32 bufSz) { WOLFSSH_BUFFER* inputBuffer; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_stream_peek()"); if (ssh == NULL || ssh->channelList == NULL) return WS_BAD_ARGUMENT; if (ssh->isKeying) { ssh->error = WS_REKEYING; return WS_REKEYING; } if (ssh->channelList->eofRxd) { ssh->error = WS_EOF; return WS_ERROR; } inputBuffer = &ssh->channelList->inputBuffer; bufSz = min(bufSz, inputBuffer->length - inputBuffer->idx); if (buf != NULL) { WMEMCPY(buf, inputBuffer->buffer + inputBuffer->idx, bufSz); } return bufSz; } static int _UpdateChannelWindow(WOLFSSH_CHANNEL* channel); /* Wrapper function for ease of use to get data after it has been decrypted from * the SSH connection. This function handles low level operations in addition to * the read, such as window adjustment and high water checking. * * In non blocking mode use the function wolfSSH_get_error(ssh) to check for * WS_WANT_READ / WS_WANT_WRITE after a fail case was hit with * wolfSSH_stream_read(). * * Returns the number of bytes read on success, negative values on fail */ int wolfSSH_stream_read(WOLFSSH* ssh, byte* buf, word32 bufSz) { int ret = WS_SUCCESS; WOLFSSH_BUFFER* inputBuffer; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_stream_read()"); if (ssh == NULL || buf == NULL || bufSz == 0 || ssh->channelList == NULL) return WS_BAD_ARGUMENT; if (ssh->channelList->eofRxd) { ssh->error = WS_EOF; return WS_ERROR; } inputBuffer = &ssh->channelList->inputBuffer; ssh->error = WS_SUCCESS; if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, " Stream read index of %u", inputBuffer->idx); WLOG(WS_LOG_DEBUG, " Stream read ava data %u", inputBuffer->length); while (inputBuffer->length - inputBuffer->idx == 0) { WLOG(WS_LOG_DEBUG, "Starting to recieve data at current index of %u", inputBuffer->idx); ret = DoReceive(ssh); if (ssh->channelList == NULL || ssh->channelList->eofRxd) ret = WS_EOF; if (ret < 0 && ret != WS_CHAN_RXD) { break; } if (ssh->error == WS_CHAN_RXD) { if (ssh->lastRxId != ssh->channelList->channel) { ret = WS_ERROR; break; } else { ret = WS_SUCCESS; } } } } /* update internal input buffer based on data read */ if (ret == WS_SUCCESS) { int n; n = min(bufSz, inputBuffer->length - inputBuffer->idx); if (n <= 0) ret = WS_BUFFER_E; else { WMEMCPY(buf, inputBuffer->buffer + inputBuffer->idx, n); ret = _UpdateChannelWindow(ssh->channelList); if (ret == WS_SUCCESS) { inputBuffer->idx += n; ret = n; } } } WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_stream_read(), rxd = %d", ret); return ret; } int wolfSSH_stream_send(WOLFSSH* ssh, byte* buf, word32 bufSz) { int bytesTxd = 0; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_stream_send()"); if (ssh == NULL || buf == NULL || ssh->channelList == NULL) return WS_BAD_ARGUMENT; if (ssh->isKeying) { ssh->error = WS_REKEYING; return WS_REKEYING; } bytesTxd = SendChannelData(ssh, ssh->channelList->channel, buf, bufSz); WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_stream_send(), txd = %d", bytesTxd); return bytesTxd; } int wolfSSH_ChannelIdSend(WOLFSSH* ssh, word32 channelId, byte* buf, word32 bufSz) { WOLFSSH_CHANNEL* channel; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelIdSend(), ID = %u", channelId); if (ssh == NULL || buf == 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; } else { if (!channel->openConfirmed) { WLOG(WS_LOG_DEBUG, "Channel not confirmed yet."); ret = WS_CHANNEL_NOT_CONF; } } } if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "Sending data."); ret = SendChannelData(ssh, channelId, buf, bufSz); } WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChannelIdSend(), txd = %d", ret); return ret; } 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; } int wolfSSH_global_request(WOLFSSH *ssh, const unsigned char* data, word32 dataSz, int reply) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_global_request"); if (ssh == NULL || data == NULL) return WS_BAD_ARGUMENT; if (reply != 0 && reply != 1) return WS_BAD_ARGUMENT; return SendGlobalRequest(ssh, data, dataSz, reply); } /* Reads pending data from extended data buffer. Currently can be used to get * STDERR information sent across the channel. * Returns the number of bytes read on success */ int wolfSSH_extended_data_read(WOLFSSH* ssh, byte* out, word32 outSz) { byte* buf; word32 bufSz; if (ssh == NULL || out == NULL) { return WS_BAD_ARGUMENT; } /* sanity check to make sure idx is not in a bad state */ if (ssh->extDataBuffer.idx > ssh->extDataBuffer.length) { WLOG(WS_LOG_ERROR, "Bad internal state for buffer index"); return WS_INVALID_STATE_E; } bufSz = min(outSz, ssh->extDataBuffer.length - ssh->extDataBuffer.idx); buf = ssh->extDataBuffer.buffer + ssh->extDataBuffer.idx; WMEMCPY(out, buf, bufSz); ssh->extDataBuffer.idx += bufSz; return bufSz; } int wolfSSH_SendIgnore(WOLFSSH* ssh, const byte* buf, word32 bufSz) { byte scratch[128]; WOLFSSH_UNUSED(buf); WOLFSSH_UNUSED(bufSz); WMEMSET(scratch, 0, sizeof(scratch)); return SendIgnore(ssh, scratch, sizeof(scratch)); } int wolfSSH_SendDisconnect(WOLFSSH *ssh, word32 reason) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SendDisconnect"); return SendDisconnect(ssh, reason); } void wolfSSH_SetUserAuth(WOLFSSH_CTX* ctx, WS_CallbackUserAuth cb) { if (ctx != NULL) { ctx->userAuthCb = cb; } } void wolfSSH_SetUserAuthTypes(WOLFSSH_CTX* ctx, WS_CallbackUserAuthTypes cb) { if (ctx != NULL) { ctx->userAuthTypesCb = cb; } } void wolfSSH_SetUserAuthCtx(WOLFSSH* ssh, void* userAuthCtx) { if (ssh != NULL) { ssh->userAuthCtx = userAuthCtx; } } void* wolfSSH_GetUserAuthCtx(WOLFSSH* ssh) { if (ssh != NULL) { return ssh->userAuthCtx; } return NULL; } void wolfSSH_SetUserAuthResult(WOLFSSH_CTX* ctx, WS_CallbackUserAuthResult cb) { if (ctx != NULL) { ctx->userAuthResultCb = cb; } } void wolfSSH_SetUserAuthResultCtx(WOLFSSH* ssh, void* userAuthResultCtx) { if (ssh != NULL) { ssh->userAuthResultCtx = userAuthResultCtx; } } void* wolfSSH_GetUserAuthResultCtx(WOLFSSH* ssh) { if (ssh != NULL) { return ssh->userAuthResultCtx; } return NULL; } void wolfSSH_CTX_SetPublicKeyCheck(WOLFSSH_CTX* ctx, WS_CallbackPublicKeyCheck cb) { if (ctx != NULL) { ctx->publicKeyCheckCb = cb; } } void wolfSSH_SetPublicKeyCheckCtx(WOLFSSH* ssh, void* publicKeyCheckCtx) { if (ssh != NULL) { ssh->publicKeyCheckCtx = publicKeyCheckCtx; } } void* wolfSSH_GetPublicKeyCheckCtx(WOLFSSH* ssh) { if (ssh != NULL) { return ssh->publicKeyCheckCtx; } return NULL; } #ifdef WOLFSSH_TERM #if defined(WOLFSSH_TERM) && !defined(NO_FILESYSTEM) /* Used to resize terminal window with shell connections * returns WS_SUCCESS on success */ int wolfSSH_ChangeTerminalSize(WOLFSSH* ssh, word32 columns, word32 rows, word32 widthPixels, word32 heightPixels) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChangeWindowDimension()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { ret = SendChannelTerminalResize(ssh, columns, rows, widthPixels, heightPixels); } WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChangeWindowDimension(), ret = %d", ret); return ret; } void wolfSSH_SetTerminalResizeCb(WOLFSSH* ssh, WS_CallbackTerminalSize cb) { ssh->termResizeCb = cb; } void wolfSSH_SetTerminalResizeCtx(WOLFSSH* ssh, void* usrCtx) { ssh->termCtx = usrCtx; } #endif #endif /* Used to set the channel request type sent in wolfSSH connect. The default * type set is shell if this function is not called. * * type channel type i.e. WOLFSSH_SESSION_SUBSYSTEM * name name or command in the case of subsystem and exec channel types * nameSz size of name buffer * * returns WS_SUCCESS on success */ int wolfSSH_SetChannelType(WOLFSSH* ssh, byte type, byte* name, word32 nameSz) { if (ssh == NULL) { return WS_BAD_ARGUMENT; } switch (type) { case WOLFSSH_SESSION_SHELL: ssh->connectChannelId = type; break; case WOLFSSH_SESSION_EXEC: if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) { WLOG(WS_LOG_DEBUG, "Server side exec unsupported"); return WS_BAD_ARGUMENT; } NO_BREAK; case WOLFSSH_SESSION_SUBSYSTEM: ssh->connectChannelId = type; if (name != NULL && nameSz < WOLFSSH_MAX_CHN_NAMESZ) { WMEMCPY(ssh->channelName, name, nameSz); ssh->channelNameSz = nameSz; } else { WLOG(WS_LOG_DEBUG, "No subsystem name or name was too large"); } break; #ifdef WOLFSSH_TERM case WOLFSSH_SESSION_TERMINAL: /* send a pseudo-terminal request and shell channel */ ssh->sendTerminalRequest = 1; ssh->connectChannelId = WOLFSSH_SESSION_SHELL; break; #endif default: WLOG(WS_LOG_DEBUG, "Unknown channel type"); return WS_BAD_ARGUMENT; } return WS_SUCCESS; } int wolfSSH_SetUsernameRaw(WOLFSSH* ssh, const byte* username, word32 usernameSz) { char* newUsername = NULL; int ret = WS_SUCCESS; if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (username == NULL || usernameSz == 0) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { newUsername = (char*)WMALLOC(usernameSz + 1, ssh->ctx->heap, DYNTYPE_STRING); if (newUsername == NULL) ret = WS_MEMORY_E; else { WMEMCPY(newUsername, username, usernameSz); newUsername[usernameSz] = 0; if (ssh->userName != NULL) WFREE(ssh->userName, ssh->ctx->heap, DYNTYPE_STRING); ssh->userName = newUsername; ssh->userNameSz = usernameSz; } } return ret; } int wolfSSH_SetUsername(WOLFSSH* ssh, const char* username) { int ret = WS_SUCCESS; if (ssh == NULL || username == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { ret = wolfSSH_SetUsernameRaw(ssh, (const byte*)username, (word32)WSTRLEN(username)); } return ret; } char* wolfSSH_GetUsername(WOLFSSH* ssh) { char* name = NULL; if (ssh != NULL) name = ssh->userName; return name; } #include #include #include union wolfSSH_key { #ifndef WOLFSSH_NO_RSA RsaKey rsa; #endif #ifndef WOLFSSH_NO_ECDSA ecc_key ecc; #endif }; /* Reads a key from the buffer in to out. If the out buffer doesn't exist it is created. The type of key is stored in outType. It'll be a pointer to a constant string. Format indicates the format of the key, currently either SSH format (a public key) or ASN.1 in DER or PEM format (a private key). */ int wolfSSH_ReadKey_buffer(const byte* in, word32 inSz, int format, byte** out, word32* outSz, const byte** outType, word32* outTypeSz, void* heap) { int ret = WS_SUCCESS; byte* newKey = NULL; WOLFSSH_UNUSED(heap); if (in == NULL || inSz == 0 || out == NULL || outSz == NULL || outType == NULL || outTypeSz == NULL) return WS_BAD_ARGUMENT; if (format == WOLFSSH_FORMAT_SSH) { char* c; char* last; char* type = NULL; char* key = NULL; /* SSH format is: type AAAABASE64ENCODEDKEYDATA comment */ c = WSTRDUP((const char*)in, heap, DYNTYPE_STRING); type = WSTRTOK(c, " \n", &last); key = WSTRTOK(NULL, " \n", &last); if (type != NULL && key != NULL) { const char* name; word32 typeSz; byte nameId; typeSz = (word32)WSTRLEN(type); nameId = NameToId(type, typeSz); name = IdToName(nameId); *outType = (const byte*)name; *outTypeSz = typeSz; if (*out == NULL) { /* set size based on sanity check in wolfSSL base64 decode * function */ *outSz = ((word32)WSTRLEN(key) * 3 + 3) / 4; newKey = (byte*)WMALLOC(*outSz, heap, DYNTYPE_PRIVKEY); if (newKey == NULL) { return WS_MEMORY_E; } *out = newKey; } ret = Base64_Decode((byte*)key, (word32)WSTRLEN(key), *out, outSz); } if (ret != 0) { WLOG(WS_LOG_DEBUG, "Base64 decode of public key failed."); ret = WS_PARSE_E; } WFREE(c, heap, DYNTYPE_STRING); } else if (format == WOLFSSH_FORMAT_ASN1) { if (*out == NULL) { newKey = (byte*)WMALLOC(inSz, heap, DYNTYPE_PRIVKEY); if (newKey == NULL) { return WS_MEMORY_E; } *out = newKey; } else { if (*outSz < inSz) { WLOG(WS_LOG_DEBUG, "DER private key output size too small"); return WS_BUFFER_E; } newKey = *out; } *outSz = inSz; WMEMCPY(newKey, in, inSz); ret = IdentifyKey(in, inSz, 1, heap); if (ret > 0) { *outType = (const byte*)IdToName(ret); *outTypeSz = (word32)WSTRLEN((const char*)*outType); ret = WS_SUCCESS; } } else if (format == WOLFSSH_FORMAT_PEM) { word32 newKeySz = inSz; /* binary will be smaller than PEM */ if (*out == NULL) { newKey = (byte*)WMALLOC(newKeySz, heap, DYNTYPE_PRIVKEY); if (newKey == NULL) { return WS_MEMORY_E; } *out = newKey; } else { if (*outSz < inSz) { WLOG(WS_LOG_DEBUG, "PEM private key output size too small"); return WS_BUFFER_E; } newKey = *out; } /* If it is PEM, convert to ASN1 then process. */ ret = wc_KeyPemToDer(in, inSz, newKey, newKeySz, NULL); if (ret > 0) { newKeySz = (word32)ret; ret = WS_SUCCESS; } else { WLOG(WS_LOG_DEBUG, "Base64 decode of public key failed."); ret = WS_PARSE_E; } if (ret == WS_SUCCESS) { ret = IdentifyKey(newKey, newKeySz, 1, heap); if (ret > 0) { *outSz = newKeySz; *outType = (const byte*)IdToName(ret); *outTypeSz = (word32)WSTRLEN((const char*)*outType); ret = WS_SUCCESS; } else { WLOG(WS_LOG_DEBUG, "unable to identify key"); } } } else { WLOG(WS_LOG_DEBUG, "Invalid key format"); ret = WS_BAD_ARGUMENT; } return ret; } #if !defined(NO_FILESYSTEM) && !defined(WOLFSSH_USER_FILESYSTEM) /* Reads a key from the file name into a buffer. If the key starts with the string "ssh-rsa" or "ecdsa-sha2-nistp256", it is considered an SSH format public key, if it has "----BEGIN" it is considered PEM formatted, otherwise it is considered an ASN.1 private key. The buffer is passed to wolfSSH_ReadKey_buffer() for processing. */ int wolfSSH_ReadKey_file(const char* name, byte** out, word32* outSz, const byte** outType, word32* outTypeSz, byte* isPrivate, void* heap) { WFILE* file; byte* in; word32 inSz; int format; int ret; if (name == NULL) return WS_BAD_FILE_E; if (out == NULL || outSz == NULL || outType == NULL || outTypeSz == NULL || isPrivate == NULL) return WS_BAD_ARGUMENT; ret = WFOPEN(NULL, &file, name, "rb"); if (ret != 0 || file == WBADFILE) return WS_BAD_FILE_E; if (WFSEEK(NULL, file, 0, WSEEK_END) != 0) { WFCLOSE(NULL, file); return WS_BAD_FILE_E; } inSz = (word32)WFTELL(NULL, file); WREWIND(NULL, file); if (inSz > WOLFSSH_MAX_FILE_SIZE || inSz == 0) { WFCLOSE(NULL, file); return WS_BAD_FILE_E; } in = (byte*)WMALLOC(inSz + 1, heap, DYNTYPE_FILE); if (in == NULL) { WFCLOSE(NULL, file); return WS_MEMORY_E; } ret = (int)WFREAD(NULL, in, 1, inSz, file); if (ret <= 0 || (word32)ret != inSz) { ret = WS_BAD_FILE_E; } else { if (WSTRNSTR((const char*)in, "ssh-rsa", inSz) == (const char*)in || WSTRNSTR((const char*)in, "ecdsa-sha2-nistp", inSz) == (const char*)in) { *isPrivate = 0; format = WOLFSSH_FORMAT_SSH; in[inSz] = 0; } else if ((WSTRNSTR((const char*)in, "-----BEGIN ", inSz) == (const char*)in) && (WSTRNSTR((const char*)in, "PRIVATE KEY-----", inSz) != NULL)) { *isPrivate = 1; format = WOLFSSH_FORMAT_PEM; } else { *isPrivate = 1; format = WOLFSSH_FORMAT_ASN1; } ret = wolfSSH_ReadKey_buffer(in, inSz, format, out, outSz, outType, outTypeSz, heap); } WFCLOSE(ssh->fs, file); WFREE(in, heap, DYNTYPE_FILE); return ret; } #endif int wolfSSH_CTX_SetBanner(WOLFSSH_CTX* ctx, const char* newBanner) { word32 newBannerSz = 0; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_SetBanner()"); if (ctx == NULL) return WS_BAD_ARGUMENT; if (newBanner != NULL) { WLOG(WS_LOG_INFO, " setting banner to: \"%s\"", newBanner); newBannerSz = (word32)WSTRLEN(newBanner); } ctx->banner = newBanner; ctx->bannerSz = newBannerSz; return WS_SUCCESS; } int wolfSSH_CTX_SetSshProtoIdStr(WOLFSSH_CTX* ctx, const char* protoIdStr) { if (!ctx || !protoIdStr) { return WS_BAD_ARGUMENT; } ctx->sshProtoIdStr = protoIdStr; return WS_SUCCESS; } int wolfSSH_CTX_UsePrivateKey_buffer(WOLFSSH_CTX* ctx, const byte* in, word32 inSz, int format) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_UsePrivateKey_buffer()"); ret = wolfSSH_ProcessBuffer(ctx, in, inSz, format, BUFTYPE_PRIVKEY); WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_CTX_UsePrivateKey_buffer(), ret = %d", ret); return ret; } #ifdef WOLFSSH_CERTS /* load in a X509 certificate that has public key to use * return WS_SUCCESS on success */ int wolfSSH_CTX_UseCert_buffer(WOLFSSH_CTX* ctx, const byte* cert, word32 certSz, int format) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_UseCert_buffer()"); ret = wolfSSH_ProcessBuffer(ctx, cert, certSz, format, BUFTYPE_CERT); WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_CTX_UseCert_buffer(), ret = %d", ret); return ret; } /* Add a CA for verifiying the peer's certificate with. * returns WS_SUCCESS on success */ int wolfSSH_CTX_AddRootCert_buffer(WOLFSSH_CTX* ctx, const byte* cert, word32 certSz, int format) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_AddRootCert_buffer()"); ret = wolfSSH_ProcessBuffer(ctx, cert, certSz, format, BUFTYPE_CA); WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_CTX_AddRootCert_buffer(), ret = %d", ret); return ret; } #endif /* WOLFSSH_CERTS */ int wolfSSH_CTX_SetWindowPacketSize(WOLFSSH_CTX* ctx, word32 windowSz, word32 maxPacketSz) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_SetWindowPacketSize()"); if (ctx == NULL) return WS_BAD_ARGUMENT; if (windowSz == 0) windowSz = DEFAULT_WINDOW_SZ; if (maxPacketSz == 0) maxPacketSz = DEFAULT_MAX_PACKET_SZ; ctx->windowSz = windowSz; ctx->maxPacketSz = maxPacketSz; return WS_SUCCESS; } void wolfSSH_GetStats(WOLFSSH* ssh, word32* txCount, word32* rxCount, word32* seq, word32* peerSeq) { word32 rTxCount = 0; word32 rRxCount = 0; word32 rSeq = 0; word32 rPeerSeq = 0; if (ssh != NULL) { rTxCount = ssh->txCount; rRxCount = ssh->rxCount; rSeq = ssh->seq; rPeerSeq = ssh->peerSeq; } if (txCount != NULL) *txCount = rTxCount; if (rxCount != NULL) *rxCount = rRxCount; if (seq != NULL) *seq = rSeq; if (peerSeq != NULL) *peerSeq = rPeerSeq; } int wolfSSH_KDF(byte hashId, byte keyId, byte* key, word32 keySz, const byte* k, word32 kSz, const byte* h, word32 hSz, const byte* sessionId, word32 sessionIdSz) { int doKeyPadding = 1; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_KDF()"); return GenerateKey(hashId, keyId, key, keySz, k, kSz, h, hSz, sessionId, sessionIdSz, doKeyPadding); } WS_SessionType wolfSSH_GetSessionType(const WOLFSSH* ssh) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_GetSessionType()"); if (ssh && ssh->channelList) return (WS_SessionType)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; } int wolfSSH_worker(WOLFSSH* ssh, word32* channelId) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_worker()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; /* Attempt to send any data pending in the outputBuffer. */ if (ret == WS_SUCCESS) { if (ssh->outputBuffer.length != 0) ret = wolfSSH_SendPacket(ssh); } /* Attempt to receive data from the peer. */ if (ret == WS_SUCCESS) { ret = DoReceive(ssh); } if (ret == WS_SUCCESS) { if (channelId != NULL) { *channelId = ssh->lastRxId; } if (ssh->isKeying) { ssh->error = WS_REKEYING; return WS_REKEYING; } } if (ret == WS_CHAN_RXD) { WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_worker(), " "data received on channel %u", ssh->lastRxId); } else { WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_worker(), ret = %d", ret); } return ret; } int wolfSSH_GetLastRxId(WOLFSSH* ssh, word32* channelId) { int ret = WS_SUCCESS; if (ssh == NULL || channelId == NULL) ret = WS_ERROR; if (ret == WS_SUCCESS) *channelId = ssh->lastRxId; return ret; } #ifdef WOLFSSH_FWD int wolfSSH_CTX_SetFwdCb(WOLFSSH_CTX* ctx, WS_CallbackFwd fwdCb, WS_CallbackFwdIO fwdIoCb) { int ret = WS_SUCCESS; if (ctx == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { ctx->fwdCb = fwdCb; ctx->fwdIoCb = fwdIoCb; } return ret; } int wolfSSH_SetFwdCbCtx(WOLFSSH* ssh, void* ctx) { int ret = WS_SUCCESS; if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { ssh->fwdCbCtx = ctx; } return ret; } WOLFSSH_CHANNEL* wolfSSH_ChannelFwdNewLocal(WOLFSSH* ssh, const char* host, word32 hostPort, const char* origin, word32 originPort) { WOLFSSH_CHANNEL* newChannel = NULL; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelFwdNewLocal()"); if (ssh == NULL || ssh->ctx == NULL || host == NULL || origin == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { newChannel = ChannelNew(ssh, ID_CHANTYPE_TCPIP_DIRECT, ssh->ctx->windowSz, ssh->ctx->maxPacketSz); if (newChannel == NULL) ret = WS_MEMORY_E; } if (ret == WS_SUCCESS) ret = ChannelUpdateForward(newChannel, host, hostPort, origin, originPort, 1); if (ret == WS_SUCCESS) ret = SendChannelOpenForward(ssh, newChannel); if (ret != WS_SUCCESS) { void* heap = (ssh != NULL && ssh->ctx != NULL) ? ssh->ctx->heap : NULL; ChannelDelete(newChannel, heap); newChannel = NULL; } if (newChannel != NULL) ChannelAppend(ssh, newChannel); WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChannelFwdNewLocal(), newChannel = %p", newChannel); return newChannel; } WOLFSSH_CHANNEL* wolfSSH_ChannelFwdNewRemote(WOLFSSH* ssh, const char* host, word32 hostPort, const char* origin, word32 originPort) { WOLFSSH_CHANNEL* newChannel = NULL; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelFwdNewRemote()"); if (ssh == NULL || ssh->ctx == NULL || host == NULL || origin == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { newChannel = ChannelNew(ssh, ID_CHANTYPE_TCPIP_FORWARD, ssh->ctx->windowSz, ssh->ctx->maxPacketSz); if (newChannel == NULL) ret = WS_MEMORY_E; } if (ret == WS_SUCCESS) ret = ChannelUpdateForward(newChannel, host, hostPort, origin, originPort, 0); if (ret == WS_SUCCESS) ret = SendChannelOpenForward(ssh, newChannel); if (ret == WS_SUCCESS) { if (ssh->ctx->fwdCb) { ret = ssh->ctx->fwdCb(WOLFSSH_FWD_CHANNEL_ID, ssh->fwdCbCtx, NULL, newChannel->channel); } } if (ret != WS_SUCCESS) { void* heap = (ssh != NULL && ssh->ctx != NULL) ? ssh->ctx->heap : NULL; ChannelDelete(newChannel, heap); newChannel = NULL; } if (newChannel != NULL) ChannelAppend(ssh, newChannel); WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChannelFwdNewRemote(), newChannel = %p, ret = %d", newChannel, ret); return newChannel; } WOLFSSH_CHANNEL* wolfSSH_ChannelFwdNew(WOLFSSH* ssh, const char* host, word32 hostPort, const char* origin, word32 originPort) { return wolfSSH_ChannelFwdNewLocal(ssh, host, hostPort, origin, originPort); } #endif /* WOLFSSH_FWD */ int wolfSSH_ChannelFree(WOLFSSH_CHANNEL* channel) { int ret; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelFree()"); if (channel != NULL) { ret = ChannelRemove(channel->ssh, channel->channel, WS_CHANNEL_ID_SELF); } else ret = WS_BAD_ARGUMENT; WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChannelFree(), ret = %d", ret); return ret; } int wolfSSH_ChannelGetId(WOLFSSH_CHANNEL* channel, word32* id, byte peer) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelGetId()"); if (channel == NULL || id == NULL) ret = WS_BAD_ARGUMENT; else { *id = (peer == WS_CHANNEL_ID_SELF) ? channel->channel : channel->peerChannel; } WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChannelGetId(), ret = %d", ret); return ret; } WOLFSSH_CHANNEL* wolfSSH_ChannelFind(WOLFSSH* ssh, word32 id, byte peer) { return ChannelFind(ssh, id, peer); } #ifdef WOLFSSH_FWD int wolfSSH_ChannelSetFwdFd(WOLFSSH_CHANNEL* channel, int fwdFd) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelSetFwdFd()"); if (channel != NULL) channel->fwdFd = fwdFd; else ret = WS_BAD_ARGUMENT; WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChannelSetFwdFd(), ret = %d", ret); return ret; } int wolfSSH_ChannelGetFwdFd(const WOLFSSH_CHANNEL* channel) { int fwdFd = -1; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelGetFwdFd()"); if (channel != NULL) fwdFd = channel->fwdFd; WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChannelGetFwdFd(), ret = %d", fwdFd); return fwdFd; } #endif /* WOLFSSH_FWD */ /* moves the window for more room * returns WS_SUCCESS on success */ static int _UpdateChannelWindow(WOLFSSH_CHANNEL* channel) { WOLFSSH_BUFFER* inputBuffer; int sendResult = WS_SUCCESS; if (channel == NULL) return WS_BAD_ARGUMENT; inputBuffer = &channel->inputBuffer; if ((inputBuffer->length > inputBuffer->bufferSz / 2) || (channel->windowSz == 0)) { word32 usedSz = inputBuffer->length - inputBuffer->idx; word32 bytesToAdd = inputBuffer->idx; WLOG(WS_LOG_DEBUG, "Making more room: %u", usedSz); if (usedSz) { WLOG(WS_LOG_DEBUG, " ...moving data down"); WMEMMOVE(inputBuffer->buffer, inputBuffer->buffer + bytesToAdd, usedSz); } sendResult = SendChannelWindowAdjust(channel->ssh, channel->channel, bytesToAdd); WLOG(WS_LOG_INFO, " bytesToAdd = %u", bytesToAdd); WLOG(WS_LOG_INFO, " windowSz = %u", channel->windowSz); channel->windowSz += bytesToAdd; WLOG(WS_LOG_INFO, " update windowSz = %u", channel->windowSz); inputBuffer->length = usedSz; inputBuffer->idx = 0; } return sendResult; } static int _ChannelRead(WOLFSSH_CHANNEL* channel, byte* buf, word32 bufSz) { WOLFSSH_BUFFER* inputBuffer; int updateResult = WS_SUCCESS; if (channel == NULL || buf == NULL || bufSz == 0) return WS_BAD_ARGUMENT; inputBuffer = &channel->inputBuffer; bufSz = min(bufSz, inputBuffer->length - inputBuffer->idx); WMEMCPY(buf, inputBuffer->buffer + inputBuffer->idx, bufSz); inputBuffer->idx += bufSz; updateResult = _UpdateChannelWindow(channel); if (updateResult == WS_SUCCESS) updateResult = bufSz; return updateResult; } int wolfSSH_ChannelIdRead(WOLFSSH* ssh, word32 channelId, byte* buf, word32 bufSz) { WOLFSSH_CHANNEL* channel = NULL; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelIdRead()"); if (ssh == NULL || buf == NULL) return WS_BAD_ARGUMENT; channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF); if (channel == NULL) return WS_INVALID_CHANID; bufSz = _ChannelRead(channel, buf, bufSz); WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChannelIdRead(), rxd = %d", bufSz); return bufSz; } int wolfSSH_ChannelRead(WOLFSSH_CHANNEL* channel, byte* buf, word32 bufSz) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelRead()"); if (channel == NULL || buf == NULL || bufSz == 0) return WS_BAD_ARGUMENT; bufSz = _ChannelRead(channel, buf, bufSz); WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChannelRead(), bytesRxd = %d", bufSz); return bufSz; } int wolfSSH_ChannelSend(WOLFSSH_CHANNEL* channel, const byte* buf, word32 bufSz) { int bytesTxd = 0; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelSend(), ID = %d, peerID = %d", channel->channel, channel->peerChannel); #ifdef DEBUG_WOLFSSH DumpOctetString(buf, bufSz); #endif if (channel == NULL || buf == NULL) bytesTxd = WS_BAD_ARGUMENT; else if (!channel->openConfirmed) { WLOG(WS_LOG_DEBUG, "Channel not confirmed yet."); bytesTxd = WS_CHANNEL_NOT_CONF; } else { WLOG(WS_LOG_DEBUG, "Sending data."); bytesTxd = SendChannelData(channel->ssh, channel->channel, (byte*)buf, bufSz); } WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChannelSend(), bytesTxd = %d", bytesTxd); return bytesTxd; } int wolfSSH_ChannelExit(WOLFSSH_CHANNEL* channel) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelExit()"); if (channel == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) ret = SendChannelEof(channel->ssh, channel->peerChannel); if (ret == WS_SUCCESS) ret = SendChannelClose(channel->ssh, channel->peerChannel); if (ret == WS_SUCCESS) ret = ChannelRemove(channel->ssh, channel->peerChannel, WS_CHANNEL_ID_PEER); WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChannelExit(), ret = %d", ret); return ret; } WOLFSSH_CHANNEL* wolfSSH_ChannelNext(WOLFSSH* ssh, WOLFSSH_CHANNEL* channel) { WOLFSSH_CHANNEL* nextChannel = NULL; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelNext()"); if (ssh != NULL && channel == NULL) nextChannel = ssh->channelList; else if (channel != NULL) nextChannel = channel->next; WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChannelNext(), %s", nextChannel == NULL ? "none" : "NEXT!"); return nextChannel; } int wolfSSH_ChannelGetEof(WOLFSSH_CHANNEL* channel) { int eof = 1; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelGetEof()"); if (channel) eof = (int)channel->eofRxd; WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChannelGetEof(), %s", eof ? "true" : "false"); return eof; } #if (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP)) && \ !defined(NO_WOLFSSH_SERVER) #define DELIM "/\\" #define IS_DELIM(x) ((x) == '/' || (x) == '\\') #define IS_WINPATH(x,y) ((x) > 1 && (y)[1] == ':') /* * Paths starting with a slash are absolute, rooted at "/". Any path that * doesn't have a starting slash is assumed to be relative to the default * path. If the path is empty, return the default path. * * The path "/." is stripped out. The path "/.." strips out the previous * path value. The root path, "/", is always present. * * Example: "/home/fred/frob/frizz/../../../barney/bar/baz/./././../.." * will return "/home/barney". "/../.." will return "/". "." will return * the default path. * * Note, this function does not care about OS and filesystem issues. The * SFTP protocol describes how paths are handled in SFTP. Specialized * behaviors are handled when actually calling the OS functions. Paths * are further massaged there. For example, the C: drive is treated as * the path "/C:", and is a directory like any other. * * @param defaultPath RealPath of the default directory, usually user's * @param in requested new path * @param out output of real path cleanup * @param outSz size in bytes of buffer 'out' * @return WS_SUCCESS, WS_BAD_ARGUMENT, or WS_INVALID_PATH_E */ int wolfSSH_RealPath(const char* defaultPath, char* in, char* out, word32 outSz) { char* tail = NULL; char* seg; word32 inSz, segSz, curSz; if (in == NULL || out == NULL || outSz == 0) { return WS_BAD_ARGUMENT; } WMEMSET(out, 0, outSz); inSz = (word32)WSTRLEN(in); out[0] = '/'; curSz = 1; if (inSz == 0 || (!IS_DELIM(in[0]) && !IS_WINPATH(inSz, in))) { if (defaultPath != NULL) { curSz = (word32)WSTRLEN(defaultPath); if (curSz >= outSz) { return WS_INVALID_PATH_E; } WSTRNCPY(out, defaultPath, outSz); } } out[curSz] = 0; for (seg = WSTRTOK(in, DELIM, &tail); seg; seg = WSTRTOK(NULL, DELIM, &tail)) { segSz = (word32)WSTRLEN(seg); /* Try to match "." */ if (segSz == 1 && seg[0] == '.') { /* Do nothing. Keep current directory. */ } /* Try to match ".." */ else if (segSz == 2 && seg[0] == '.' && seg[1] == '.') { char* prev = strrchr(out, '/'); if (prev != NULL) { if (prev != out #ifdef WOLFSSH_ZEPHYR /* Zephyr FAT fs path names follow the format of '/RAM:' * and we want to preserve the '/' after this mount * point definition too. */ && prev[-1] != ':' #endif ) { prev[0] = 0; curSz = (word32)WSTRLEN(out); } else { /* preserve the root / */ prev[1] = 0; curSz = 1; } } } /* Everything else is copied */ else { if (curSz >= outSz - segSz) { return WS_INVALID_PATH_E; } if (curSz != 1) { WSTRNCAT(out, "/", outSz - curSz); curSz++; } WSTRNCAT(out, seg, outSz - curSz); curSz += segSz; } } return WS_SUCCESS; } #endif /* WOLFSSH_SFTP || WOLFSSH_SCP */ #ifdef WOLFSSH_SHOW_SIZES void wolfSSH_ShowSizes(void) { fprintf(stderr, "wolfSSH struct sizes:\n"); fprintf(stderr, " sizeof(struct %s) = %u\n", "WOLFSSH_CTX", (word32)sizeof(struct WOLFSSH_CTX)); fprintf(stderr, " sizeof(struct %s) = %u\n", "WOLFSSH", (word32)sizeof(struct WOLFSSH)); fprintf(stderr, " sizeof(struct %s) = %u\n", "HandshakeInfo", (word32)sizeof(struct HandshakeInfo)); fprintf(stderr, " sizeof(struct %s) = %u\n", "WOLFSSH_CHANNEL", (word32)sizeof(struct WOLFSSH_CHANNEL)); fprintf(stderr, " sizeof(struct %s) = %u\n", "Buffer", (word32)sizeof(struct Buffer)); #ifdef WOLFSSH_SFTP wolfSSH_SFTP_ShowSizes(); #endif } #endif /* WOLFSSH_SHOW_SIZES */