OVMS3/OVMS.V3/components/wolfssh/examples/portfwd/portfwd.c

485 lines
14 KiB
C

/* portfwd.c
*
* Copyright (C) 2014-2020 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 <http://www.gnu.org/licenses/>.
*/
#define WOLFSSH_TEST_CLIENT
#define WOLFSSH_TEST_SERVER
#include <stdio.h>
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
#include <errno.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include <wolfssh/ssh.h>
#include <wolfssh/test.h>
#include <wolfssh/port.h>
#include <wolfssl/wolfcrypt/ecc.h>
#include "examples/portfwd/wolfssh_portfwd.h"
/* The portfwd tool will be a client or server in the port forwarding
* interaction.
*
* The portfwd client will connect to an SSH server and request a tunnel.
* The client acts like a server for the local user application. It forwards
* the packets received to the SSH server who will then forward the packet
* to its local application server.
*
* The portfwd server will listen for SSH connections, and when it receives
* one will only accept forward requests from the connection. All data for
* the forwarding channel are sent to the local application server and
* data from the server is forwarded to the client.
*/
#define INVALID_FWD_PORT 0
static const char defaultFwdFromHost[] = "0.0.0.0";
static inline int max(int a, int b)
{
return (a > b) ? a : b;
}
static void ShowUsage(void)
{
printf("portfwd %s\n"
" -? display this help and exit\n"
" -h <host> host to connect to, default %s\n"
" -p <num> port to connect on, default %u\n"
" -u <username> username to authenticate as (REQUIRED)\n"
" -P <password> password for username, prompted if omitted\n"
" -F <host> host to forward from, default %s\n"
" -f <num> host port to forward from (REQUIRED)\n"
" -T <host> host to forward to, default to host\n"
" -t <num> port to forward to (REQUIRED)\n",
LIBWOLFSSH_VERSION_STRING,
wolfSshIp, wolfSshPort, defaultFwdFromHost);
}
static int SetEcho(int on)
{
#ifndef USE_WINDOWS_API
static int echoInit = 0;
static struct termios originalTerm;
if (!echoInit) {
if (tcgetattr(STDIN_FILENO, &originalTerm) != 0) {
printf("Couldn't get the original terminal settings.\n");
return -1;
}
echoInit = 1;
}
if (on) {
if (tcsetattr(STDIN_FILENO, TCSANOW, &originalTerm) != 0) {
printf("Couldn't restore the terminal settings.\n");
return -1;
}
}
else {
struct termios newTerm;
memcpy(&newTerm, &originalTerm, sizeof(struct termios));
newTerm.c_lflag &= ~ECHO;
newTerm.c_lflag |= (ICANON | ECHONL);
if (tcsetattr(STDIN_FILENO, TCSANOW, &newTerm) != 0) {
printf("Couldn't turn off echo.\n");
return -1;
}
}
#else
static int echoInit = 0;
static DWORD originalTerm;
HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
if (!echoInit) {
if (GetConsoleMode(stdinHandle, &originalTerm) == 0) {
printf("Couldn't get the original terminal settings.\n");
return -1;
}
echoInit = 1;
}
if (on) {
if (SetConsoleMode(stdinHandle, originalTerm) == 0) {
printf("Couldn't restore the terminal settings.\n");
return -1;
}
}
else {
DWORD newTerm = originalTerm;
newTerm &= ~ENABLE_ECHO_INPUT;
if (SetConsoleMode(stdinHandle, newTerm) == 0) {
printf("Couldn't turn off echo.\n");
return -1;
}
}
#endif
return 0;
}
byte userPassword[256];
static int wsUserAuth(byte authType,
WS_UserAuthData* authData,
void* ctx)
{
const char* defaultPassword = (const char*)ctx;
word32 passwordSz;
int ret = WOLFSSH_USERAUTH_SUCCESS;
(void)authType;
if (defaultPassword != NULL) {
passwordSz = (word32)strlen(defaultPassword);
memcpy(userPassword, defaultPassword, passwordSz);
}
else {
printf("Password: ");
SetEcho(0);
if (WFGETS((char*)userPassword, sizeof(userPassword), stdin) == NULL) {
printf("Getting password failed.\n");
ret = WOLFSSH_USERAUTH_FAILURE;
}
else {
char* c = strpbrk((char*)userPassword, "\r\n");;
if (c != NULL)
*c = '\0';
passwordSz = (word32)strlen((const char*)userPassword);
}
SetEcho(1);
#ifdef USE_WINDOWS_API
printf("\r\n");
#endif
}
if (ret == WOLFSSH_USERAUTH_SUCCESS) {
authData->sf.password.password = userPassword;
authData->sf.password.passwordSz = passwordSz;
}
return ret;
}
static int wsPublicKeyCheck(const byte* pubKey, word32 pubKeySz, void* ctx)
{
printf("Sample public key check callback\n"
" public key = %p\n"
" public key size = %u\n"
" ctx = %s\n", pubKey, pubKeySz, (const char*)ctx);
return 0;
}
/*
* fwdFromHost - address to bind the local listener socket to (default: any)
* fwdFromHostPort - port number to bind the local listener socket to
* fwdToHost - address to tell the remote peer to connect to on behalf of the
* client (actual server address)
* fwdToHostPort - port number to tell the remote peer to connect to on behalf
* of the client (actual server port)
* host - peer SSH server address to connect to
* hostPort - peer SSH server port number to connect to
*/
THREAD_RETURN WOLFSSH_THREAD portfwd_worker(void* args)
{
WOLFSSH* ssh;
WOLFSSH_CTX* ctx;
char* host = (char*)wolfSshIp;
word16 port = wolfSshPort;
word16 fwdFromPort = INVALID_FWD_PORT;
word16 fwdToPort = INVALID_FWD_PORT;
const char* fwdFromHost = defaultFwdFromHost;
const char* fwdToHost = NULL;
const char* username = NULL;
const char* password = NULL;
SOCKADDR_IN_T hostAddr;
socklen_t hostAddrSz = sizeof(hostAddr);
SOCKET_T sshFd;
SOCKADDR_IN_T fwdFromHostAddr;
socklen_t fwdFromHostAddrSz = sizeof(fwdFromHostAddr);
SOCKET_T listenFd;
SOCKET_T appFd = -1;
int argc = ((func_args*)args)->argc;
char** argv = ((func_args*)args)->argv;
fd_set templateFds;
fd_set rxFds;
fd_set errFds;
int nFds;
int ret;
int ch;
int appFdSet = 0;
struct timeval to;
WOLFSSH_CHANNEL* fwdChannel = NULL;
byte buffer[4096];
word32 bufferSz = sizeof(buffer);
word32 bufferUsed = 0;
((func_args*)args)->return_code = 0;
while ((ch = mygetopt(argc, argv, "?f:h:p:t:u:F:P:T:")) != -1) {
switch (ch) {
case 'h':
host = myoptarg;
break;
case 'f':
fwdFromPort = (word16)atoi(myoptarg);
break;
case 'p':
port = (word16)atoi(myoptarg);
#if !defined(NO_MAIN_DRIVER) || defined(USE_WINDOWS_API)
if (port == 0)
err_sys("port number cannot be 0");
#endif
break;
case 't':
fwdToPort = (word16)atoi(myoptarg);
break;
case 'u':
username = myoptarg;
break;
case 'F':
fwdFromHost = myoptarg;
break;
case 'P':
password = myoptarg;
break;
case 'T':
fwdToHost = myoptarg;
break;
case '?':
ShowUsage();
exit(EXIT_SUCCESS);
default:
ShowUsage();
exit(MY_EX_USAGE);
}
}
myoptind = 0;
if (username == NULL)
err_sys("client requires a username parameter.");
if (fwdToPort == INVALID_FWD_PORT)
err_sys("requires a port to forward to");
if (fwdFromPort == INVALID_FWD_PORT)
err_sys("requires a port to forward from");
if (fwdToHost == NULL)
fwdToHost = host;
printf("portfwd options\n"
" * ssh host: %s:%u\n"
" * username: %s\n"
" * password: %s\n"
" * forward from: %s:%u\n"
" * forward to: %s:%u\n",
host, port, username, password,
fwdFromHost, fwdFromPort,
fwdToHost, fwdToPort);
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
if (ctx == NULL)
err_sys("couldn't create the ssh client");
if (((func_args*)args)->user_auth == NULL)
wolfSSH_SetUserAuth(ctx, wsUserAuth);
else
wolfSSH_SetUserAuth(ctx, ((func_args*)args)->user_auth);
ssh = wolfSSH_new(ctx);
if (ssh == NULL)
err_sys("Couldn't create wolfSSH session.");
if (password != NULL)
wolfSSH_SetUserAuthCtx(ssh, (void*)password);
wolfSSH_CTX_SetPublicKeyCheck(ctx, wsPublicKeyCheck);
wolfSSH_SetPublicKeyCheckCtx(ssh, (void*)"You've been sampled.");
ret = wolfSSH_SetUsername(ssh, username);
if (ret != WS_SUCCESS)
err_sys("Couldn't set the username.");
build_addr(&hostAddr, host, port);
build_addr(&fwdFromHostAddr, fwdFromHost, fwdFromPort);
tcp_socket(&sshFd); /* Socket to SSH peer. */
tcp_socket(&listenFd); /* Either receive from client application or connect
to server application. */
tcp_listen(&listenFd, &fwdFromPort, 1);
printf("Connecting to the SSH server...\n");
ret = connect(sshFd, (const struct sockaddr *)&hostAddr, hostAddrSz);
if (ret != 0)
err_sys("Couldn't connect to server.");
ret = wolfSSH_set_fd(ssh, (int)sshFd);
if (ret != WS_SUCCESS)
err_sys("Couldn't set the session's socket.");
ret = wolfSSH_connect(ssh);
if (ret != WS_SUCCESS)
err_sys("Couldn't connect SFTP");
FD_ZERO(&templateFds);
FD_SET(sshFd, &templateFds);
FD_SET(listenFd, &templateFds);
nFds = max(sshFd, listenFd) + 1;
for (;;) {
rxFds = templateFds;
errFds = templateFds;
to.tv_sec = 1;
to.tv_usec = 0;
ret = select(nFds, &rxFds, NULL, &errFds, &to);
if (ret == 0) {
ret = wolfSSH_SendIgnore(ssh, NULL, 0);
if (ret != WS_SUCCESS)
err_sys("Couldn't send an ignore message.");
continue;
}
else if (ret < 0)
err_sys("select failed\n");
if ((appFdSet && FD_ISSET(appFd, &errFds)) ||
FD_ISSET(sshFd, &errFds) ||
FD_ISSET(listenFd, &errFds)) {
err_sys("some socket had an error");
}
if (appFdSet && FD_ISSET(appFd, &rxFds)) {
int rxd;
rxd = (int)recv(appFd,
buffer + bufferUsed, bufferSz - bufferUsed, 0);
if (rxd > 0)
bufferUsed += rxd;
else
break;
}
if (FD_ISSET(sshFd, &rxFds)) {
word32 channelId = 0;
ret = wolfSSH_worker(ssh, &channelId);
if (ret == WS_CHAN_RXD) {
WOLFSSH_CHANNEL* readChannel;
bufferSz = sizeof(buffer);
readChannel = wolfSSH_ChannelFind(ssh,
channelId, WS_CHANNEL_ID_SELF);
ret = (readChannel == NULL) ? WS_INVALID_CHANID : WS_SUCCESS;
if (ret == WS_SUCCESS)
ret = wolfSSH_ChannelRead(readChannel, buffer, bufferSz);
if (ret > 0) {
bufferSz = (word32)ret;
if (appFd != -1) {
ret = (int)send(appFd, buffer, bufferSz, 0);
if (ret != (int)bufferSz)
break;
}
}
}
}
if (!appFdSet && FD_ISSET(listenFd, &rxFds)) {
appFd = accept(listenFd,
(struct sockaddr*)&fwdFromHostAddr, &fwdFromHostAddrSz);
if (appFd < 0)
break;
FD_SET(appFd, &templateFds);
nFds = appFd + 1;
appFdSet = 1;
fwdChannel = wolfSSH_ChannelFwdNew(ssh, fwdToHost, fwdToPort,
fwdFromHost, fwdFromPort);
}
if (bufferUsed > 0) {
ret = wolfSSH_ChannelSend(fwdChannel, buffer, bufferUsed);
if (ret > 0)
bufferUsed -= ret;
}
}
ret = wolfSSH_shutdown(ssh);
if (ret != WS_SUCCESS)
err_sys("Closing port forward stream failed.");
WCLOSESOCKET(sshFd);
WCLOSESOCKET(listenFd);
WCLOSESOCKET(appFd);
wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);
#if defined(HAVE_ECC) && defined(FP_ECC) && defined(HAVE_THREAD_LS)
wc_ecc_fp_free(); /* free per thread cache */
#endif
return 0;
}
#ifndef NO_MAIN_DRIVER
int main(int argc, char** argv)
{
func_args args;
args.argc = argc;
args.argv = argv;
args.return_code = 0;
args.user_auth = NULL;
WSTARTTCP();
#ifdef DEBUG_WOLFSSH
wolfSSH_Debugging_ON();
#endif
wolfSSH_Init();
ChangeToWolfSshRoot();
portfwd_worker(&args);
wolfSSH_Cleanup();
return args.return_code;
}
int myoptind = 0;
char* myoptarg = NULL;
#endif /* NO_MAIN_DRIVER */