mirror of https://github.com/wolfSSL/wolfssh.git
897 lines
25 KiB
C
897 lines
25 KiB
C
/* wolfsshd.c
|
|
*
|
|
* Copyright (C) 2014-2021 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/>.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#ifdef WOLFSSH_SSHD
|
|
|
|
#include <wolfssh/ssh.h>
|
|
#include <wolfssh/internal.h>
|
|
#include <wolfssh/log.h>
|
|
#include <wolfssl/wolfcrypt/wc_port.h>
|
|
#include <wolfssl/wolfcrypt/error-crypt.h>
|
|
#include <wolfssl/wolfcrypt/logging.h>
|
|
#include <wolfssl/wolfcrypt/asn_public.h>
|
|
|
|
#define WOLFSSH_TEST_SERVER
|
|
#include <wolfssh/test.h>
|
|
|
|
#include "configuration.h"
|
|
#include "auth.h"
|
|
|
|
#include <signal.h>
|
|
|
|
#ifdef NO_INLINE
|
|
#include <wolfssh/misc.h>
|
|
#else
|
|
#define WOLFSSH_MISC_INCLUDED
|
|
#include "src/misc.c"
|
|
#endif
|
|
|
|
#ifndef WOLFSSHD_TIMEOUT
|
|
#define WOLFSSHD_TIMEOUT 1
|
|
#endif
|
|
|
|
#ifdef WOLFSSH_SHELL
|
|
#ifdef HAVE_PTY_H
|
|
#include <pty.h>
|
|
#endif
|
|
#ifdef HAVE_UTIL_H
|
|
#include <util.h>
|
|
#endif
|
|
#ifdef HAVE_TERMIOS_H
|
|
#include <termios.h>
|
|
#endif
|
|
#include <pwd.h>
|
|
#include <signal.h>
|
|
#if defined(__QNX__) || defined(__QNXNTO__)
|
|
#include <errno.h>
|
|
#include <unix.h>
|
|
#else
|
|
#include <sys/errno.h>
|
|
#endif
|
|
|
|
static volatile int ChildRunning = 0;
|
|
static void ChildSig(int sig)
|
|
{
|
|
(void)sig;
|
|
ChildRunning = 0;
|
|
}
|
|
#endif /* WOLFSSH_SHELL */
|
|
|
|
static volatile byte debugMode = 0; /* default to off */
|
|
|
|
/* catch interrupts and close down gracefully */
|
|
static volatile byte quit = 0;
|
|
static const char defaultBanner[] = "wolfSSHD\n";
|
|
|
|
/* Initial connection information to pass on to threads/forks */
|
|
typedef struct WOLFSSHD_CONNECTION {
|
|
WOLFSSH_CTX* ctx;
|
|
WOLFSSHD_AUTH* auth;
|
|
int fd;
|
|
} WOLFSSHD_CONNECTION;
|
|
|
|
static void ShowUsage(void)
|
|
{
|
|
printf("wolfsshd %s\n", LIBWOLFSSH_VERSION_STRING);
|
|
printf(" -? display this help and exit\n");
|
|
printf(" -f <file name> Configuration file to use, default is /usr/local/etc/ssh/sshd_config\n");
|
|
printf(" -p <int> Port number to listen on\n");
|
|
printf(" -d Turn on debug mode\n");
|
|
printf(" -h <file name> host private key file to use\n");
|
|
}
|
|
|
|
static void interruptCatch(int in)
|
|
{
|
|
(void)in;
|
|
printf("Closing down wolfSSHD\n");
|
|
quit = 1;
|
|
}
|
|
|
|
static void wolfSSHDLoggingCb(enum wolfSSH_LogLevel lvl, const char *const str)
|
|
{
|
|
if (debugMode) {
|
|
fprintf(stderr, "[PID %d]: %s\n", getpid(), str);
|
|
}
|
|
(void)lvl;
|
|
}
|
|
|
|
static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
const char* banner;
|
|
DerBuffer* der = NULL;
|
|
byte* privBuf;
|
|
word32 privBufSz;
|
|
|
|
/* create a new WOLFSSH_CTX */
|
|
*ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
|
|
if (ctx == NULL) {
|
|
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Couldn't allocate SSH CTX data.");
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
|
|
/* setup authority callback for checking peer connections */
|
|
if (ret == WS_SUCCESS) {
|
|
wolfSSH_SetUserAuth(*ctx, DefaultUserAuth);
|
|
}
|
|
|
|
/* set banner to display on connection */
|
|
if (ret == WS_SUCCESS) {
|
|
banner = wolfSSHD_ConfigGetBanner(conf);
|
|
if (banner == NULL) {
|
|
banner = defaultBanner;
|
|
}
|
|
wolfSSH_CTX_SetBanner(*ctx, banner);
|
|
}
|
|
|
|
#ifdef WOLFSSH_AGENT
|
|
/* check if using an agent is enabled */
|
|
/* TODO: doesn't work
|
|
if (ret == WS_SUCCESS) {
|
|
wolfSSH_CTX_set_agent_cb(ctx, wolfSSH_AGENT_DefaultActions, NULL);
|
|
}
|
|
*/
|
|
#endif
|
|
|
|
#ifdef WOLFSSH_FWD
|
|
/* check if port forwarding is enabled */
|
|
/* TODO: doesn't work
|
|
if (ret == WS_SUCCESS) {
|
|
wolfSSH_CTX_SetFwdCb(ctx, wolfSSH_FwdDefaultActions, NULL);
|
|
}
|
|
*/
|
|
#endif
|
|
|
|
/* Load in host private key */
|
|
if (ret == WS_SUCCESS) {
|
|
|
|
char* hostKey = wolfSSHD_ConfigGetHostKeyFile(conf);
|
|
|
|
if (hostKey == NULL) {
|
|
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] No host private key set");
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
else {
|
|
FILE* f;
|
|
|
|
f = XFOPEN(hostKey, "rb");
|
|
if (f == NULL) {
|
|
wolfSSH_Log(WS_LOG_ERROR,
|
|
"[SSHD] Unable to open host private key");
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
else {
|
|
byte* data;
|
|
int dataSz = 4096;
|
|
|
|
data = (byte*)WMALLOC(dataSz, NULL, 0);
|
|
|
|
dataSz = (int)XFREAD(data, 1, dataSz, f);
|
|
XFCLOSE(f);
|
|
|
|
if (wc_PemToDer(data, dataSz, PRIVATEKEY_TYPE, &der, NULL,
|
|
NULL, NULL) != 0) {
|
|
wolfSSH_Log(WS_LOG_DEBUG, "[SSHD] Failed to convert host "
|
|
"private key from PEM. Assuming key in DER "
|
|
"format.");
|
|
privBuf = data;
|
|
privBufSz = dataSz;
|
|
}
|
|
else {
|
|
privBuf = der->buffer;
|
|
privBufSz = der->length;
|
|
}
|
|
|
|
if (wolfSSH_CTX_UsePrivateKey_buffer(*ctx, privBuf, privBufSz,
|
|
WOLFSSH_FORMAT_ASN1) < 0) {
|
|
wolfSSH_Log(WS_LOG_ERROR,
|
|
"[SSHD] Failed to use host private key.");
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
WFREE(data, NULL, 0);
|
|
wc_FreeDer(&der);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Load in host public key */
|
|
// {
|
|
// if (userPubKey) {
|
|
// byte* userBuf = NULL;
|
|
// word32 userBufSz = 0;
|
|
//
|
|
// /* get the files size */
|
|
// load_file(userPubKey, NULL, &userBufSz);
|
|
//
|
|
// /* create temp buffer and load in file */
|
|
// if (userBufSz == 0) {
|
|
// fprintf(stderr, "Couldn't find size of file %s.\n", userPubKey);
|
|
// WEXIT(EXIT_FAILURE);
|
|
// }
|
|
//
|
|
// userBuf = (byte*)WMALLOC(userBufSz, NULL, 0);
|
|
// if (userBuf == NULL) {
|
|
// fprintf(stderr, "WMALLOC failed\n");
|
|
// WEXIT(EXIT_FAILURE);
|
|
// }
|
|
// load_file(userPubKey, userBuf, &userBufSz);
|
|
// LoadPublicKeyBuffer(userBuf, userBufSz, &pwMapList);
|
|
// }
|
|
//
|
|
// bufSz = (word32)WSTRLEN(samplePasswordBuffer);
|
|
// WMEMCPY(keyLoadBuf, samplePasswordBuffer, bufSz);
|
|
// keyLoadBuf[bufSz] = 0;
|
|
// LoadPasswordBuffer(keyLoadBuf, bufSz, &pwMapList);
|
|
//
|
|
// if (userEcc) {
|
|
// #ifndef WOLFSSH_NO_ECC
|
|
// bufName = samplePublicKeyEccBuffer;
|
|
// #endif
|
|
// }
|
|
// else {
|
|
// #ifndef WOLFSSH_NO_RSA
|
|
// bufName = samplePublicKeyRsaBuffer;
|
|
// #endif
|
|
// }
|
|
// if (bufName != NULL) {
|
|
// bufSz = (word32)WSTRLEN(bufName);
|
|
// WMEMCPY(keyLoadBuf, bufName, bufSz);
|
|
// keyLoadBuf[bufSz] = 0;
|
|
// LoadPublicKeyBuffer(keyLoadBuf, bufSz, &pwMapList);
|
|
// }
|
|
//
|
|
// bufSz = (word32)WSTRLEN(sampleNoneBuffer);
|
|
// WMEMCPY(keyLoadBuf, sampleNoneBuffer, bufSz);
|
|
// keyLoadBuf[bufSz] = 0;
|
|
// LoadNoneBuffer(keyLoadBuf, bufSz, &pwMapList);
|
|
//
|
|
// #ifdef WOLFSSH_SMALL_STACK
|
|
// WFREE(keyLoadBuf, NULL, 0);
|
|
// #endif
|
|
// }
|
|
|
|
/* Set allowed connection type, i.e. public key / password */
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifdef WOLFSSH_SFTP
|
|
#define TEST_SFTP_TIMEOUT 1
|
|
|
|
/* handle SFTP operations
|
|
* returns 0 on success
|
|
*/
|
|
static int SFTP_Subsystem(WOLFSSH* ssh, WOLFSSHD_CONNECTION* conn)
|
|
{
|
|
byte tmp[1];
|
|
int ret = WS_SUCCESS;
|
|
int error = WS_SUCCESS;
|
|
WS_SOCKET_T sockfd;
|
|
int select_ret = 0;
|
|
|
|
sockfd = (WS_SOCKET_T)wolfSSH_get_fd(ssh);
|
|
do {
|
|
// if (threadCtx->nonBlock) {
|
|
// if (error == WS_WANT_READ)
|
|
// printf("... sftp server would read block\n");
|
|
// else if (error == WS_WANT_WRITE)
|
|
// printf("... sftp server would write block\n");
|
|
// }
|
|
|
|
if (wolfSSH_stream_peek(ssh, tmp, 1) > 0) {
|
|
select_ret = WS_SELECT_RECV_READY;
|
|
}
|
|
else {
|
|
select_ret = tcp_select(sockfd, TEST_SFTP_TIMEOUT);
|
|
}
|
|
|
|
if (select_ret == WS_SELECT_RECV_READY ||
|
|
select_ret == WS_SELECT_ERROR_READY ||
|
|
error == WS_WANT_WRITE)
|
|
{
|
|
ret = wolfSSH_SFTP_read(ssh);
|
|
error = wolfSSH_get_error(ssh);
|
|
}
|
|
else if (select_ret == WS_SELECT_TIMEOUT)
|
|
error = WS_WANT_READ;
|
|
else
|
|
error = WS_FATAL_ERROR;
|
|
|
|
if (error == WS_WANT_READ || error == WS_WANT_WRITE ||
|
|
error == WS_CHAN_RXD || error == WS_REKEYING ||
|
|
error == WS_WINDOW_FULL)
|
|
ret = error;
|
|
|
|
if (ret == WS_FATAL_ERROR && error == 0) {
|
|
WOLFSSH_CHANNEL* channel =
|
|
wolfSSH_ChannelNext(ssh, NULL);
|
|
if (channel && wolfSSH_ChannelGetEof(channel)) {
|
|
ret = 0;
|
|
break;
|
|
}
|
|
}
|
|
} while (ret != WS_FATAL_ERROR);
|
|
|
|
(void)conn;
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef WOLFSSH_SHELL
|
|
static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh)
|
|
{
|
|
WS_SOCKET_T sshFd = 0;
|
|
int rc;
|
|
const char *userName;
|
|
struct passwd *p_passwd;
|
|
WS_SOCKET_T childFd = 0;
|
|
pid_t childPid;
|
|
#ifndef EXAMPLE_BUFFER_SZ
|
|
#define EXAMPLE_BUFFER_SZ 4096
|
|
#endif
|
|
byte shellBuffer[EXAMPLE_BUFFER_SZ];
|
|
byte channelBuffer[EXAMPLE_BUFFER_SZ];
|
|
|
|
userName = wolfSSH_GetUsername(ssh);
|
|
|
|
/* temporarily elevate permissions to get users information */
|
|
if (wolfSSHD_AuthRaisePermissions(conn->auth) != 0) {
|
|
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failure to raise permissions for auth");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
p_passwd = getpwnam((const char *)userName);
|
|
if (p_passwd == NULL) {
|
|
/* Not actually a user on the system. */
|
|
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Invalid user name found");
|
|
if (wolfSSHD_AuthReducePermissions(conn->auth) != 0) {
|
|
/* stop everything if not able to reduce permissions level */
|
|
exit(1);
|
|
}
|
|
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
ChildRunning = 1;
|
|
childPid = forkpty(&childFd, NULL, NULL, NULL);
|
|
if (wolfSSHD_AuthReducePermissions(conn->auth) != 0) {
|
|
/* stop everything if not able to reduce permissions level */
|
|
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue reducing permissions level,"
|
|
" exiting now");
|
|
exit(1);
|
|
}
|
|
|
|
if (childPid < 0) {
|
|
/* forkpty failed, so return */
|
|
ChildRunning = 0;
|
|
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue creating new forkpty");
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
else if (childPid == 0) {
|
|
/* Child process */
|
|
const char *args[] = {"-sh", NULL};
|
|
char cmd[80];
|
|
int ret;
|
|
|
|
signal(SIGINT, SIG_DFL);
|
|
|
|
ret = setgid(p_passwd->pw_gid);
|
|
if (ret) {
|
|
printf("Error executing setgid\n");
|
|
}
|
|
ret = setuid(p_passwd->pw_uid);
|
|
if (ret) {
|
|
printf("Error executing setuid\n");
|
|
}
|
|
if (system("env") != 0) {
|
|
printf("0 return value from system call\n");
|
|
}
|
|
|
|
setenv("HOME", p_passwd->pw_dir, 1);
|
|
setenv("LOGNAME", p_passwd->pw_name, 1);
|
|
|
|
/* @TODO this needs reworked, can just exit into root */
|
|
WMEMSET(cmd, 0, sizeof(cmd));
|
|
XSNPRINTF(cmd, sizeof(cmd), "su %s", userName);
|
|
printf("executing command [%s]\n", cmd);
|
|
errno = 0;
|
|
ret = system(cmd);
|
|
if (ret && errno) {
|
|
printf("error executing command\n");
|
|
}
|
|
rc = chdir(p_passwd->pw_dir);
|
|
if (rc != 0) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
execv("/bin/sh", (char **)args);
|
|
}
|
|
sshFd = wolfSSH_get_fd(ssh);
|
|
|
|
struct termios tios;
|
|
word32 shellChannelId = 0;
|
|
signal(SIGCHLD, ChildSig);
|
|
|
|
rc = tcgetattr(childFd, &tios);
|
|
if (rc != 0) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
rc = tcsetattr(childFd, TCSAFLUSH, &tios);
|
|
if (rc != 0) {
|
|
return WS_FATAL_ERROR;
|
|
}
|
|
|
|
while (ChildRunning) {
|
|
byte tmp[2];
|
|
fd_set readFds;
|
|
WS_SOCKET_T maxFd;
|
|
int cnt_r;
|
|
int cnt_w;
|
|
int pending = 0;
|
|
|
|
FD_ZERO(&readFds);
|
|
FD_SET(sshFd, &readFds);
|
|
maxFd = sshFd;
|
|
|
|
FD_SET(childFd, &readFds);
|
|
if (childFd > maxFd)
|
|
maxFd = childFd;
|
|
|
|
if (wolfSSH_stream_peek(ssh, tmp, 1) <= 0) {
|
|
rc = select((int)maxFd + 1, &readFds, NULL, NULL, NULL);
|
|
if (rc == -1)
|
|
break;
|
|
}
|
|
else {
|
|
pending = 1; /* found some pending SSH data */
|
|
}
|
|
|
|
if (pending || FD_ISSET(sshFd, &readFds)) {
|
|
word32 lastChannel = 0;
|
|
|
|
/* The following tries to read from the first channel inside
|
|
the stream. If the pending data in the socket is for
|
|
another channel, this will return an error with id
|
|
WS_CHAN_RXD. That means the agent has pending data in its
|
|
channel. The additional channel is only used with the
|
|
agent. */
|
|
cnt_r = wolfSSH_worker(ssh, &lastChannel);
|
|
if (cnt_r < 0) {
|
|
rc = wolfSSH_get_error(ssh);
|
|
if (rc == WS_CHAN_RXD) {
|
|
if (lastChannel == shellChannelId) {
|
|
cnt_r = wolfSSH_ChannelIdRead(ssh, shellChannelId,
|
|
channelBuffer,
|
|
sizeof channelBuffer);
|
|
if (cnt_r <= 0)
|
|
break;
|
|
cnt_w = (int)write(childFd,
|
|
channelBuffer, cnt_r);
|
|
if (cnt_w <= 0)
|
|
break;
|
|
}
|
|
}
|
|
else if (rc == WS_CHANNEL_CLOSED) {
|
|
continue;
|
|
}
|
|
else if (rc != WS_WANT_READ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FD_ISSET(childFd, &readFds)) {
|
|
cnt_r = (int)read(childFd, shellBuffer, sizeof shellBuffer);
|
|
/* This read will return 0 on EOF */
|
|
if (cnt_r <= 0) {
|
|
int err = errno;
|
|
if (err != EAGAIN) {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (cnt_r > 0) {
|
|
cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId,
|
|
shellBuffer, cnt_r);
|
|
if (cnt_w < 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
(void)conn;
|
|
return WS_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
static __thread int timeOut = 0;
|
|
static void alarmCatch(int signum)
|
|
{
|
|
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failed login within grace period");
|
|
timeOut = 1;
|
|
(void)signum;
|
|
}
|
|
|
|
/* handle wolfSSH accept and directing to correct subsystem */
|
|
static void* HandleConnection(void* arg)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
|
|
WOLFSSHD_CONNECTION* conn = NULL;
|
|
WOLFSSH* ssh = NULL;
|
|
|
|
if (arg == NULL) {
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
conn = (WOLFSSHD_CONNECTION*)arg;
|
|
ssh = wolfSSH_new(conn->ctx);
|
|
if (ssh == NULL) {
|
|
wolfSSH_Log(WS_LOG_ERROR,
|
|
"[SSHD] Failed to create new WOLFSSH struct");
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
int error;
|
|
int select_ret = 0;
|
|
long graceTime;
|
|
|
|
wolfSSH_set_fd(ssh, conn->fd);
|
|
wolfSSH_SetUserAuthCtx(ssh, conn->auth);
|
|
|
|
/* set alarm for login grace time */
|
|
graceTime = wolfSSHD_AuthGetGraceTime(conn->auth);
|
|
if (graceTime > 0) {
|
|
signal(SIGALRM, alarmCatch);
|
|
alarm(graceTime);
|
|
}
|
|
|
|
ret = wolfSSH_accept(ssh);
|
|
error = wolfSSH_get_error(ssh);
|
|
while (timeOut == 0 && (ret != WS_SUCCESS
|
|
&& ret != WS_SCP_COMPLETE && ret != WS_SFTP_COMPLETE)
|
|
&& (error == WS_WANT_READ || error == WS_WANT_WRITE)) {
|
|
|
|
select_ret = tcp_select(conn->fd, 1);
|
|
if (select_ret == WS_SELECT_RECV_READY ||
|
|
select_ret == WS_SELECT_ERROR_READY ||
|
|
error == WS_WANT_WRITE)
|
|
{
|
|
ret = wolfSSH_accept(ssh);
|
|
error = wolfSSH_get_error(ssh);
|
|
}
|
|
else if (select_ret == WS_SELECT_TIMEOUT)
|
|
error = WS_WANT_READ;
|
|
else
|
|
error = WS_FATAL_ERROR;
|
|
}
|
|
|
|
if (ret != WS_SUCCESS && ret != WS_SFTP_COMPLETE) {
|
|
wolfSSH_Log(WS_LOG_ERROR,
|
|
"[SSHD] Failed to accept WOLFSSH connection");
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS || ret == WS_SFTP_COMPLETE) {
|
|
switch (wolfSSH_GetSessionType(ssh)) {
|
|
case WOLFSSH_SESSION_SHELL:
|
|
#ifdef WOLFSSH_SHELL
|
|
if (ret == WS_SUCCESS) {
|
|
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Entering new shell");
|
|
SHELL_Subsystem(conn, ssh);
|
|
}
|
|
#else
|
|
wolfSSH_Log(WS_LOG_ERROR,
|
|
"[SSHD] Shell support is disabled");
|
|
ret = WS_NOT_COMPILED;
|
|
#endif
|
|
break;
|
|
|
|
case WOLFSSH_SESSION_SUBSYSTEM:
|
|
/* test for known subsystems */
|
|
switch (ret) {
|
|
case WS_SFTP_COMPLETE:
|
|
#ifdef WOLFSSH_SFTP
|
|
ret = SFTP_Subsystem(ssh, conn);
|
|
#else
|
|
err_sys("SFTP not compiled in. Please use --enable-sftp");
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
wolfSSH_Log(WS_LOG_ERROR,
|
|
"[SSHD] Unknown or build not supporting subsystem"
|
|
" found [%s]", wolfSSH_GetSessionCommand(ssh));
|
|
ret = WS_NOT_COMPILED;
|
|
}
|
|
break;
|
|
|
|
case WOLFSSH_SESSION_UNKNOWN:
|
|
case WOLFSSH_SESSION_EXEC:
|
|
case WOLFSSH_SESSION_TERMINAL:
|
|
default:
|
|
wolfSSH_Log(WS_LOG_ERROR,
|
|
"[SSHD] Unknown or build not supporting session type found");
|
|
ret = WS_NOT_COMPILED;
|
|
}
|
|
}
|
|
|
|
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Attempting to close down connection");
|
|
wolfSSH_shutdown(ssh);
|
|
wolfSSH_free(ssh);
|
|
if (conn != NULL) {
|
|
WCLOSESOCKET(conn->fd);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* returns WS_SUCCESS on success */
|
|
static int NewConnection(WOLFSSHD_CONNECTION* conn)
|
|
{
|
|
int pd;
|
|
int ret = WS_SUCCESS;
|
|
|
|
pd = fork();
|
|
if (pd < 0) {
|
|
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue spawning new process");
|
|
ret = -1;
|
|
}
|
|
|
|
if (pd == 0) {
|
|
/* child process */
|
|
(void)HandleConnection((void*)conn);
|
|
}
|
|
else {
|
|
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Spawned new process %d\n", pd);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* return non zero value for a pending connection */
|
|
static int PendingConnection(WS_SOCKET_T fd)
|
|
{
|
|
int ret;
|
|
struct timeval t;
|
|
fd_set r, w, e;
|
|
WS_SOCKET_T nfds = fd + 1;
|
|
|
|
t.tv_usec = 0;
|
|
t.tv_sec = WOLFSSHD_TIMEOUT;
|
|
|
|
FD_ZERO(&r);
|
|
FD_ZERO(&w);
|
|
FD_ZERO(&e);
|
|
|
|
FD_SET(fd, &r);
|
|
ret = select(nfds, &r, &w, &e, &t);
|
|
if (ret < 0) {
|
|
/* a socket level issue happend */
|
|
printf("Error waiting for connection on socket\n");
|
|
quit = 1;
|
|
ret = 0;
|
|
}
|
|
else if (ret > 0) {
|
|
if (FD_ISSET(fd, &r)) {
|
|
printf("Connection found\n");
|
|
}
|
|
else {
|
|
printf("Found write or error data\n");
|
|
ret = 0; /* nothing to read */
|
|
}
|
|
}
|
|
// printf("Timeout waiting for connection\n");
|
|
return ret;
|
|
}
|
|
|
|
|
|
int myoptind = 0;
|
|
char* myoptarg = NULL;
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
int ret = WS_SUCCESS;
|
|
word16 port = 0;
|
|
WS_SOCKET_T listenFd = 0;
|
|
int ch;
|
|
WOLFSSHD_CONFIG* conf = NULL;
|
|
WOLFSSHD_AUTH* auth = NULL;
|
|
WOLFSSH_CTX* ctx = NULL;
|
|
|
|
const char* configFile = "/usr/local/etc/ssh/sshd_config";
|
|
const char* hostKeyFile = NULL;
|
|
|
|
signal(SIGINT, interruptCatch);
|
|
|
|
wolfSSH_SetLoggingCb(wolfSSHDLoggingCb);
|
|
#ifdef DEBUG_WOLFSSH
|
|
wolfSSH_Debugging_ON();
|
|
#endif
|
|
#ifdef DEBUG_WOLFSSL
|
|
wolfSSL_Debugging_ON();
|
|
#endif
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
wolfSSH_Init();
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
conf = wolfSSHD_ConfigNew(NULL);
|
|
if (conf == NULL) {
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
}
|
|
|
|
while ((ch = mygetopt(argc, argv, "?f:p:h:d")) != -1) {
|
|
switch (ch) {
|
|
case 'f':
|
|
configFile = myoptarg;
|
|
break;
|
|
|
|
case 'p':
|
|
if (ret == WS_SUCCESS) {
|
|
ret = XATOI(myoptarg);
|
|
if (ret < 0) {
|
|
printf("Issue parsing port number %s\n", myoptarg);
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
else {
|
|
if (ret <= (word16)-1) {
|
|
port = (word16)ret;
|
|
ret = WS_SUCCESS;
|
|
}
|
|
else {
|
|
printf("Port number %d too big.\n", ret);
|
|
ret = WS_BAD_ARGUMENT;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'h':
|
|
hostKeyFile = myoptarg;
|
|
break;
|
|
|
|
case 'd':
|
|
debugMode = 1; /* turn on debug mode */
|
|
break;
|
|
|
|
case '?':
|
|
ShowUsage();
|
|
return WS_SUCCESS;
|
|
|
|
default:
|
|
ShowUsage();
|
|
return WS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = wolfSSHD_ConfigLoad(conf, configFile);
|
|
if (ret != WS_SUCCESS)
|
|
printf("Error reading in configure file %s\n", configFile);
|
|
}
|
|
|
|
/* port was not overridden with argument, read from config file */
|
|
if (port == 0) {
|
|
port = wolfSSHD_ConfigGetPort(conf);
|
|
}
|
|
|
|
/* check if host key file was passed in */
|
|
if (hostKeyFile != NULL) {
|
|
wolfSSHD_ConfigSetHostKeyFile(conf, hostKeyFile);
|
|
}
|
|
|
|
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Starting wolfSSH SSHD application");
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
ret = SetupCTX(conf, &ctx);
|
|
}
|
|
else {
|
|
/* TODO: handle error. */
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
auth = wolfSSHD_AuthCreateUser(NULL, conf);
|
|
if (auth == NULL) {
|
|
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue creating auth struct");
|
|
ret = WS_MEMORY_E;
|
|
}
|
|
}
|
|
|
|
/* seperate privlage permisions */
|
|
if (ret == WS_SUCCESS) {
|
|
if (wolfSSHD_AuthReducePermissions(auth) != 0) {
|
|
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Error lowering permissions level");
|
|
ret = WS_FATAL_ERROR;
|
|
}
|
|
}
|
|
|
|
if (ret == WS_SUCCESS) {
|
|
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Starting to listen on port %d", port);
|
|
tcp_listen(&listenFd, &port, 1);
|
|
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Listening on port %d", port);
|
|
|
|
/* wait for incoming connections and fork them off */
|
|
while (ret == WS_SUCCESS && quit == 0) {
|
|
WOLFSSHD_CONNECTION conn;
|
|
#ifdef WOLFSSL_NUCLEUS
|
|
struct addr_struct clientAddr;
|
|
#else
|
|
SOCKADDR_IN_T clientAddr;
|
|
socklen_t clientAddrSz = sizeof(clientAddr);
|
|
#endif
|
|
conn.auth = auth;
|
|
|
|
/* wait for a connection */
|
|
if (PendingConnection(listenFd)) {
|
|
conn.ctx = ctx;
|
|
#ifdef WOLFSSL_NUCLEUS
|
|
conn.fd = NU_Accept(listenFd, &clientAddr, 0);
|
|
#else
|
|
conn.fd = accept(listenFd, (struct sockaddr*)&clientAddr,
|
|
&clientAddrSz);
|
|
#endif
|
|
|
|
{
|
|
#ifdef USE_WINDOWS_API
|
|
unsigned long blocking = 1;
|
|
int ret = ioctlsocket(conn.fd, FIONBIO, &blocking);
|
|
if (ret == SOCKET_ERROR)
|
|
err_sys("ioctlsocket failed");
|
|
#elif defined(WOLFSSL_MDK_ARM) || defined(WOLFSSL_KEIL_TCP_NET) \
|
|
|| defined (WOLFSSL_TIRTOS)|| defined(WOLFSSL_VXWORKS) || \
|
|
defined(WOLFSSL_NUCLEUS)
|
|
/* non blocking not supported, for now */
|
|
#else
|
|
int flags = fcntl(conn.fd, F_GETFL, 0);
|
|
if (flags < 0)
|
|
err_sys("fcntl get failed");
|
|
flags = fcntl(conn.fd, F_SETFL, flags | O_NONBLOCK);
|
|
if (flags < 0)
|
|
err_sys("fcntl set failed");
|
|
#endif
|
|
}
|
|
ret = NewConnection(&conn);
|
|
}
|
|
}
|
|
}
|
|
|
|
wolfSSHD_ConfigFree(conf);
|
|
wolfSSHD_AuthFreeUser(auth);
|
|
wolfSSH_Cleanup();
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSH_SSHD */
|