diff --git a/apps/wolfsshd/wolfsshd.c b/apps/wolfsshd/wolfsshd.c index 5dad0343..39ac8939 100644 --- a/apps/wolfsshd/wolfsshd.c +++ b/apps/wolfsshd/wolfsshd.c @@ -108,10 +108,15 @@ static volatile byte quit = 0; typedef struct WOLFSSHD_CONNECTION { WOLFSSH_CTX* ctx; WOLFSSHD_AUTH* auth; + WOLFSSH_CHANNEL* channel; int fd; int listenFd; char ip[INET6_ADDRSTRLEN]; - byte isThreaded; + byte isThreaded:1; + byte doShell:1; + byte doSftp:1; + byte doScp:1; + byte doExec:1; } WOLFSSHD_CONNECTION; #ifdef __unix__ @@ -179,6 +184,50 @@ static void ServiceDebugCb(enum wolfSSH_LogLevel level, const char* const msgStr #endif #endif /* _WIN32 */ +static int wsChannelReqCb(WOLFSSH_CHANNEL* channel, void* vCtx) +{ + WOLFSSHD_CONNECTION* ctx; + const char* cmd; + WS_SessionType type; + + if (!vCtx || !channel) { + return 0; + } + + ctx = (WOLFSSHD_CONNECTION*)vCtx; + cmd = wolfSSH_ChannelGetSessionCommand(channel); + type = wolfSSH_ChannelGetSessionType(channel); + +#ifdef WOLFSSH_SHELL + if (type == WOLFSSH_SESSION_SHELL) { + ctx->channel = channel; + ctx->doShell = 1; + return 0; + } +#endif +#ifdef WOLFSSH_SCP + if (type == WOLFSSH_SESSION_EXEC) { + ctx->channel = channel; + ctx->doExec = 1; + ctx->doScp = WSTRSTR(cmd, "scp") != NULL; + return 0; + } +#endif +#ifdef WOLFSSH_SFTP + if (type == WOLFSSH_SESSION_SUBSYSTEM) { + if (!WSTRCMP(cmd, "sftp")) { + ctx->channel = channel; + ctx->doSftp = 1; + return 0; + } + } +#endif + + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Unknown or build not supporting session type found"); + return 1; +} + static void ShowUsage(void) { printf("wolfSSHd %s linked with wolfSSL %s\n", LIBWOLFSSH_VERSION_STRING, @@ -328,6 +377,13 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx, } } + /* set the channel request callbacks */ + if (ret == WS_SUCCESS) { + wolfSSH_CTX_SetChannelReqShellCb(*ctx, wsChannelReqCb); + wolfSSH_CTX_SetChannelReqExecCb(*ctx, wsChannelReqCb); + wolfSSH_CTX_SetChannelReqSubsysCb(*ctx, wsChannelReqCb); + } + /* Load in host private key */ if (ret == WS_SUCCESS) { @@ -776,6 +832,9 @@ static int SFTP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, if (error == WS_EOF) break; } + else { + timeout = TEST_SFTP_TIMEOUT; + } if (ret == WS_FATAL_ERROR && error == 0) { WOLFSSH_CHANNEL* channel = @@ -994,6 +1053,135 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, WLOG(WS_LOG_ERROR, "Issue with pseudo console resize"); ret = WS_FATAL_ERROR; } + else { + do { + /* @TODO currently not blocking till data comes in */ + if (PeekNamedPipe(ptyOut, NULL, 0, NULL, &ava, NULL) == TRUE) { + if (ava > 0) { + readPending = 1; + } + } + + if (readPending == 0) { + /* check if process is still running before waiting to read */ + if (GetExitCodeProcess(processInfo.hProcess, &processState) + == TRUE) { + if (processState != STILL_ACTIVE) { + wolfSSH_Log(WS_LOG_INFO, + "[SSHD] Process has exited, exit state = %d, " + "close down SSH connection", processState); + Sleep(100); /* give the stdout/stderr of process a + * little time to write to pipe */ + if (PeekNamedPipe(ptyOut, NULL, 0, NULL, &ava, NULL) + == TRUE) { + if (ava > 0) { + /* if data still pending then continue + * sending it over SSH */ + readPending = 1; + continue; + } + } + break; + } + } + if (wolfSSH_stream_peek(ssh, tmp, 1) <= 0) { + rc = select((int)maxFd + 1, &readFds, NULL, NULL, &t); + if (rc == -1) { + wolfSSH_Log(WS_LOG_INFO, + "[SSHD] select call waiting on socket failed"); + break; + } + /* when select times out and no socket is set as ready + Windows overwrites readFds with 0. Reset the fd here + for next select call */ + if (rc == 0) { + FD_SET(sshFd, &readFds); + } + } + else { + pending = 1; + } + } + + if (rc != 0 && (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, shellBuffer, + sizeof shellBuffer); + if (cnt_r <= 0) + break; + pending = 0; + if (WriteFile(ptyIn, shellBuffer, cnt_r, &cnt_r, + NULL) != TRUE) { + wolfSSH_Log(WS_LOG_INFO, + "[SSHD] Error writing to pipe for " + "console"); + break; + } + } + } + else if (rc == WS_CHANNEL_CLOSED) { + continue; + } + else if (rc != WS_WANT_READ) { + break; + } + } + } + + if (readPending) { + WMEMSET(shellBuffer, 0, EXAMPLE_BUFFER_SZ); + + if (ReadFile(ptyOut, shellBuffer, EXAMPLE_BUFFER_SZ, &cnt_r, + NULL) != TRUE) { + wolfSSH_Log(WS_LOG_INFO, + "[SSHD] Error reading from pipe for console"); + break; + } + else { + readPending = 0; + if (cnt_r > 0) { + cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId, + shellBuffer, cnt_r); + if (cnt_w < 0) + break; + } + } + } + } while (1); + + if (cmd != NULL) { + WFREE(cmd, NULL, DYNTYPE_SSHD); + } + wolfSSH_Log(WS_LOG_INFO, + "[SSHD] Closing down process for console"); + + if (ext.lpAttributeList != NULL) { + HeapFree(GetProcessHeap(), 0, ext.lpAttributeList); + } + + if (wolfSSH_SetExitStatus(ssh, processState) != + WS_SUCCESS) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue sending childs exit " + "status"); + } + + ClosePseudoConsole(pCon); + CloseHandle(processInfo.hThread); + CloseHandle(wolfSSHD_GetAuthToken(conn->auth)); + } } if (ret == WS_SUCCESS) { @@ -1751,6 +1939,7 @@ static void* HandleConnection(void* arg) wolfSSH_set_fd(ssh, conn->fd); wolfSSH_SetUserAuthCtx(ssh, conn->auth); + wolfSSH_SetChannelReqCtx(ssh, conn); /* set alarm for login grace time */ graceTime = wolfSSHD_AuthGetGraceTime(conn->auth); @@ -1798,15 +1987,14 @@ static void* HandleConnection(void* arg) #endif } - if (ret != WS_SUCCESS && ret != WS_SFTP_COMPLETE && - ret != WS_SCP_INIT) { + if (ret != WS_SUCCESS) { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failed to accept WOLFSSH connection from %s error %d", conn->ip, ret); } } - if (ret == WS_SUCCESS || ret == WS_SFTP_COMPLETE || ret == WS_SCP_INIT) { + if (ret == WS_SUCCESS) { WPASSWD* pPasswd = NULL; WOLFSSHD_CONFIG* usrConf; char* usr; @@ -1822,8 +2010,7 @@ static void* HandleConnection(void* arg) } #ifndef WIN32 - if (ret == WS_SUCCESS || ret == WS_SFTP_COMPLETE || - ret == WS_SCP_INIT) { + if (ret == WS_SUCCESS) { pPasswd = getpwnam((const char *)usr); if (pPasswd == NULL) { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error getting user info"); @@ -1832,81 +2019,47 @@ static void* HandleConnection(void* arg) } #endif - if (ret != WS_FATAL_ERROR) { - /* check for any forced command set for the user */ - 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, pPasswd, usrConf, NULL); - } - #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(conn, ssh, pPasswd, usrConf); - #else - err_sys("SFTP not compiled in. Please use " - "--enable-sftp"); - #endif - break; - - case WS_SCP_INIT: - #ifdef WOLFSSH_SCP - ret = SCP_Subsystem(conn, ssh, pPasswd, usrConf); - #else - err_sys("SCP not compiled in. Please use " - "--enable-scp"); - #endif - break; - - default: - wolfSSH_Log(WS_LOG_ERROR, - "[SSHD] Unknown or build not supporting sub" - "system found [%s]", - wolfSSH_GetSessionCommand(ssh)); - ret = WS_NOT_COMPILED; - } - break; - - case WOLFSSH_SESSION_UNKNOWN: - case WOLFSSH_SESSION_EXEC: - #if defined(WOLFSSH_SHELL) - if (ret == WS_SUCCESS) { - wolfSSH_Log(WS_LOG_INFO, - "[SSHD] Entering exec session [%s]", - wolfSSH_GetSessionCommand(ssh)); - SHELL_Subsystem(conn, ssh, pPasswd, usrConf, - wolfSSH_GetSessionCommand(ssh)); - } - #endif /* WOLFSSH_SHELL */ - - /* SCP can be an exec type */ - if (ret == WS_SCP_INIT) { - #ifdef WOLFSSH_SCP - ret = SCP_Subsystem(conn, ssh, pPasswd, usrConf); - #else - err_sys("SCP not compiled in. Please use " - "--enable-scp"); - #endif - } - break; + do { + ret = wolfSSH_worker(ssh, NULL); + } while (conn->channel == NULL); - case WOLFSSH_SESSION_TERMINAL: - default: - wolfSSH_Log(WS_LOG_ERROR, - "[SSHD] Unknown or build not supporting session type " - "found"); - ret = WS_NOT_COMPILED; + if (conn->doShell) { + #ifdef WOLFSSH_SHELL + if (ret == WS_SUCCESS) { + SHELL_Subsystem(conn, ssh, pPasswd, usrConf, NULL); + } + #else + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Shell support is disabled"); + ret = WS_NOT_COMPILED; + #endif + } + else if (conn->doSftp) { + #ifdef WOLFSSH_SFTP + do { + ret = wolfSSH_SFTP_accept(ssh); + /* XXX Need some select */ + } while (ret != WS_SFTP_COMPLETE + && (error == WS_WANT_READ || error == WS_WANT_WRITE)); + if (ret == WS_SFTP_COMPLETE) { + ret = SFTP_Subsystem(conn, ssh, pPasswd, usrConf); + } + #else + err_sys("SFTP not compiled in. Please use --enable-sftp"); + #endif + } + else if (conn->doScp) { + #ifdef WOLFSSH_SCP + ret = SCP_Subsystem(conn, ssh, pPasswd, usrConf); + #else + err_sys("SCP not compiled in. Please use --enable-scp"); + #endif + } + else if (conn->doExec) { + if (ret == WS_SUCCESS) { + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Entering exec session [%s]", + wolfSSH_GetSessionCommand(ssh)); + SHELL_Subsystem(conn, ssh, pPasswd, usrConf, + wolfSSH_GetSessionCommand(ssh)); } } } @@ -2121,7 +2274,7 @@ static void wolfSSHD_ServiceCb(DWORD CtrlCode) static char* _convertHelper(WCHAR* in, void* heap) { int retSz; char* ret; - + retSz = (int)wcslen(in) * 2; ret = (char*)WMALLOC(retSz + 1, heap, DYNTYPE_SSHD); if (ret != NULL) { @@ -2468,7 +2621,8 @@ static int StartSSHD(int argc, char** argv) struct sockaddr_in6 clientAddr; socklen_t clientAddrSz = sizeof(clientAddr); #endif - conn = (WOLFSSHD_CONNECTION*)WMALLOC(sizeof(WOLFSSHD_CONNECTION), NULL, DYNTYPE_SSHD); + conn = (WOLFSSHD_CONNECTION*)WMALLOC(sizeof(WOLFSSHD_CONNECTION), + NULL, DYNTYPE_SSHD); if (conn == NULL) { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failed to malloc memory for connection"); break; @@ -2477,6 +2631,11 @@ static int StartSSHD(int argc, char** argv) conn->auth = auth; conn->listenFd = (int)listenFd; conn->isThreaded = isDaemon; + conn->doShell = 0; + conn->doSftp = 0; + conn->doScp= 0; + conn->doExec = 0; + conn->channel = NULL; /* wait for a connection */ if (PendingConnection(listenFd)) {