/* ssh.c * * Copyright (C) 2014-2016 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 #ifdef NO_INLINE #include #else #define WOLFSSH_MISC_INCLUDED #include "src/misc.c" #endif int wolfSSH_Init(void) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_Init()"); if (wolfCrypt_Init() != 0) ret = WS_CRYPTO_FAILED; WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_Init(), returning %d", ret); return ret; } int wolfSSH_Cleanup(void) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_Cleanup()"); WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_Cleanup(), returning %d", WS_SUCCESS); return WS_SUCCESS; } WOLFSSH_CTX* wolfSSH_CTX_new(uint8_t 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); ctx = CtxInit(ctx, heap); 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; 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; } WLOG(WS_LOG_DEBUG, "Entering wolfSSH_new()"); 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; SshResourceFree(ssh, heap); WFREE(ssh, heap, DYNTYPE_SSH); } } int wolfSSH_set_fd(WOLFSSH* ssh, int 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; } int wolfSSH_get_fd(const WOLFSSH* ssh) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_get_fd()"); if (ssh) return ssh->rfd; return WS_BAD_ARGUMENT; } int wolfSSH_SetHighwater(WOLFSSH* ssh, uint32_t highwater) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetHighwater()"); if (ssh) { ssh->highwaterMark = highwater; return WS_SUCCESS; } return WS_BAD_ARGUMENT; } uint32_t wolfSSH_GetHighwater(WOLFSSH* ssh) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_GetHighwater()"); if (ssh) return ssh->highwaterMark; return 0; } void wolfSSH_SetHighwaterCb(WOLFSSH_CTX* ctx, uint32_t 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; } int wolfSSH_get_error(const WOLFSSH* ssh) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_get_error()"); if (ssh) return ssh->error; return WS_BAD_ARGUMENT; } 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 NULL; } 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()"); switch (ssh->acceptState) { case ACCEPT_BEGIN: if ( (ssh->error = SendServerVersion(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"); case ACCEPT_SERVER_VERSION_SENT: while (ssh->clientState < CLIENT_VERSION_DONE) { if ( (ssh->error = ProcessClientVersion(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"); case ACCEPT_CLIENT_VERSION_DONE: while (ssh->keyingState < KEYING_KEYED) { if ( (ssh->error = DoReceive(ssh)) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, acceptError, "CLIENT_VERSION_DONE", ssh->error); return WS_FATAL_ERROR; } } ssh->acceptState = ACCEPT_KEYED; WLOG(WS_LOG_DEBUG, acceptState, "KEYED"); case ACCEPT_KEYED: while (ssh->clientState < CLIENT_USERAUTH_REQUEST_DONE) { if ( (ssh->error = 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"); case ACCEPT_CLIENT_USERAUTH_REQUEST_DONE: if ( (ssh->error = SendServiceAccept(ssh)) < 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"); case ACCEPT_SERVER_USERAUTH_ACCEPT_SENT: while (ssh->clientState < CLIENT_USERAUTH_DONE) { if ( (ssh->error = 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"); 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"); case ACCEPT_SERVER_USERAUTH_SENT: while (ssh->clientState < CLIENT_DONE) { if ( (ssh->error = DoReceive(ssh)) < 0) { WLOG(WS_LOG_DEBUG, acceptError, "SERVER_USERAUTH_SENT", ssh->error); return WS_FATAL_ERROR; } } ssh->acceptState = ACCEPT_CLIENT_CHANNEL_REQUEST_DONE; WLOG(WS_LOG_DEBUG, acceptState, "CLIENT_CHANNEL_REQUEST_DONE"); case ACCEPT_CLIENT_CHANNEL_REQUEST_DONE: if ( (ssh->error = SendChannelOpenConf(ssh)) < WS_SUCCESS) { WLOG(WS_LOG_DEBUG, acceptError, "CLIENT_CHANNEL_REQUEST_DONE", ssh->error); return WS_FATAL_ERROR; } ssh->acceptState = ACCEPT_SERVER_CHANNEL_ACCEPT_SENT; WLOG(WS_LOG_DEBUG, acceptState, "SERVER_CHANNEL_ACCEPT_SENT"); } return WS_SUCCESS; } 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 = SendKexInit(ssh); WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_TriggerKeyExchange(), ret = %d", ret); return ret; } int wolfSSH_stream_read(WOLFSSH* ssh, uint8_t* buf, uint32_t bufSz) { Buffer* inputBuffer; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_stream_read()"); if (ssh == NULL || buf == NULL || bufSz == 0 || ssh->channelList == NULL) return WS_BAD_ARGUMENT; inputBuffer = &ssh->channelList->inputBuffer; while (inputBuffer->length - inputBuffer->idx == 0) { int ret = DoReceive(ssh); if (ret < 0) { WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_stream_read(), ret = %d", ret); return ret; } } bufSz = min(bufSz, inputBuffer->length - inputBuffer->idx); WMEMCPY(buf, inputBuffer->buffer + inputBuffer->idx, bufSz); inputBuffer->idx += bufSz; if (ssh->keyingState == KEYING_KEYED && (inputBuffer->length > inputBuffer->bufferSz / 2)) { uint32_t usedSz = inputBuffer->length - inputBuffer->idx; uint32_t bytesToAdd = inputBuffer->idx; int sendResult; 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(ssh, ssh->channelList->peerChannel, bytesToAdd); if (sendResult != WS_SUCCESS) bufSz = sendResult; WLOG(WS_LOG_INFO, " bytesToAdd = %u", bytesToAdd); WLOG(WS_LOG_INFO, " windowSz = %u", ssh->channelList->windowSz); ssh->channelList->windowSz += bytesToAdd; WLOG(WS_LOG_INFO, " update windowSz = %u", ssh->channelList->windowSz); inputBuffer->length = usedSz; inputBuffer->idx = 0; } WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_stream_read(), rxd = %d", bufSz); return bufSz; } int wolfSSH_stream_send(WOLFSSH* ssh, uint8_t* buf, uint32_t bufSz) { int bytesTxd = 0; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_stream_send()"); if (ssh == NULL || buf == NULL || ssh->channelList == NULL) return WS_BAD_ARGUMENT; bytesTxd = SendChannelData(ssh, ssh->channelList->peerChannel, buf, bufSz); WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_stream_send(), txd = %d", bytesTxd); return bytesTxd; } void wolfSSH_SetUserAuth(WOLFSSH_CTX* ctx, WS_CallbackUserAuth cb) { if (ctx != NULL) { ctx->userAuthCb = 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; } int wolfSSH_CTX_SetBanner(WOLFSSH_CTX* ctx, const char* newBanner) { uint32_t 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 = (uint32_t)WSTRLEN(newBanner); } ctx->banner = newBanner; ctx->bannerSz = newBannerSz; return WS_SUCCESS; } int wolfSSH_CTX_UsePrivateKey_buffer(WOLFSSH_CTX* ctx, const uint8_t* in, uint32_t inSz, int format) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_UsePrivateKey_buffer()"); return ProcessBuffer(ctx, in, inSz, format, BUFTYPE_PRIVKEY); } void wolfSSH_GetStats(WOLFSSH* ssh, uint32_t* txCount, uint32_t* rxCount, uint32_t* seq, uint32_t* peerSeq) { uint32_t rTxCount = 0; uint32_t rRxCount = 0; uint32_t rSeq = 0; uint32_t 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(uint8_t hashId, uint8_t keyId, uint8_t* key, uint32_t keySz, const uint8_t* k, uint32_t kSz, const uint8_t* h, uint32_t hSz, const uint8_t* sessionId, uint32_t sessionIdSz) { WLOG(WS_LOG_DEBUG, "Entering wolfSSH_KDF()"); return GenerateKey(hashId, keyId, key, keySz, k, kSz, h, hSz, sessionId, sessionIdSz); }