1471 lines
46 KiB
C
1471 lines
46 KiB
C
/*
|
|
******************************************************************************
|
|
* SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipssh_connection.c,v $ $Revision: 1.116.10.16 $
|
|
* $Source: /home/interpeak/CVSRoot/ipssh/src/ipssh_connection.c,v $
|
|
* $Author: svc-cmnet $ $Date: 2018-09-27 23:32:54 $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Roger Boden <roger@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include <ipssh_config.h>
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include "ipssh_h.h"
|
|
#include "ipssh_transport.h"
|
|
#ifdef IPSSH_USE_V1
|
|
#include "ipssh_transport_v1.h"
|
|
#include "ipssh_connection_v1.h"
|
|
#endif
|
|
#include "ipssh_auth.h"
|
|
#include "ipssh_buffer.h"
|
|
#include "ipssh_connection.h"
|
|
#include "ipssh_sftp.h"
|
|
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_config.h>
|
|
#include <ipcom_type.h>
|
|
#include <ipcom_cstyle.h>
|
|
#include <ipcom_buffer.h>
|
|
#include <ipcom_sock.h>
|
|
#include <ipcom_sock2.h>
|
|
#include <ipcom_err.h>
|
|
|
|
#include <openssl/bn.h>
|
|
#include <openssl/hmac.h>
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 TYPES
|
|
****************************************************************************
|
|
*/
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
IP_STATIC Ip_err ipssh_start_shell(Ipssh_conn* ssh_conn, Ipssh_channel* channel);
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
Ipssh_terminal_resize_cb ipssh_terminal_resize_cb = ipssh_set_win_size;
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 LOCAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
IP_GLOBAL Ip_err
|
|
ipssh_handle_exec_cmd(Ipssh_conn* ssh_conn, Ipssh_channel* channel, const char* exec_cmd)
|
|
{
|
|
Ip_size_t bytes_written = 0;
|
|
Ip_ssize_t bytes_read;
|
|
char buf[32];
|
|
Ip_fd shell_fd;
|
|
Ip_fd_set read_set;
|
|
struct Ip_timeval timeout;
|
|
int i;
|
|
|
|
if(ipssh_start_shell(ssh_conn, channel) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_handle_exec_cmd :: ipssh_start_shell() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
shell_fd = channel->data.shell.shell_fd;
|
|
timeout.tv_sec = 1;
|
|
timeout.tv_usec = 0;
|
|
|
|
/* wait for 3 second at most. */
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
IP_FD_ZERO(&read_set);
|
|
IP_FD_SET(shell_fd, &read_set);
|
|
if (ipcom_socketselect(shell_fd + 1, &read_set, IP_NULL, IP_NULL, &timeout) > 0)
|
|
break;
|
|
}
|
|
/* read the shell data and ignore the CR and blanks. */
|
|
if (IP_FD_ISSET(shell_fd, &read_set))
|
|
{
|
|
bytes_read = ipcom_socketread(channel->data.shell.shell_fd, buf, 32);
|
|
if(bytes_read == 0 || (bytes_read < 0 && (ipcom_errno == IP_ERRNO_ESHUTDOWN || ipcom_errno == IP_ERRNO_ECONNRESET)))
|
|
{
|
|
(void)ipssh_shell_exit2(ssh_conn, shell_fd);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
if(bytes_read < 0)
|
|
{
|
|
if(!(ipcom_errno == IP_ERRNO_EWOULDBLOCK
|
|
#if (defined(IP_PORT_OSE) || defined(IP_PORT_OSE5)) && (defined(IP_TARGET_SOFT) || defined(softOSE) || defined(IP_TARGET_SOLARIS2) || defined(IP_TARGET_LINUX) || defined(IP_TARGET_WIN32))
|
|
/* When INET=api in userconf file errno is not set properly. This is a somewhat crude work-around */
|
|
|| ipcom_errno == 0
|
|
#endif
|
|
))
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
/* ignore the CR and blanks. */
|
|
}
|
|
ipcom_millisleep(10);
|
|
|
|
/* Set shell type to be IPSSH_CH_SHELL_EXEC_CMD and default return value to be */
|
|
channel->data.shell.shell_type = IPSSH_SHELL_EXEC_CMD;
|
|
channel->data.shell.exit_status_val = IPSSH_SHELL_EXEC_CMD_DEF_STATUS;
|
|
while (bytes_written < ipcom_strlen(exec_cmd))
|
|
{
|
|
int write_len;
|
|
|
|
write_len = ipcom_socketwrite(shell_fd, exec_cmd, ipcom_strlen(exec_cmd));
|
|
if (write_len < 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_handle_exec_cmd :: ipcom_socketwrite() failed, errno=%d", ipcom_errno);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
bytes_written += write_len;
|
|
if (bytes_written < ipcom_strlen(exec_cmd))
|
|
ipcom_millisleep(10);
|
|
}
|
|
if (ipcom_socketwrite(shell_fd, "\r", 1) != 1)
|
|
return IPCOM_ERR_FAILED;
|
|
|
|
if (ipcom_shutdown(shell_fd, IP_SHUT_WR) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_handle_exec_cmd :: ipcom_shutdown() failed, errno=%d", ipcom_errno);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_handle_term_modes
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipssh_handle_term_modes(Ip_u8* buf, int len, Ipssh_term_modes *term_modes)
|
|
{
|
|
int i;
|
|
|
|
for(i=0; i<len; i++)
|
|
{
|
|
if (term_modes->ICRNL)
|
|
if (buf[i] == '\r')
|
|
buf[i] = '\n';
|
|
if (term_modes->IXBSDEL)
|
|
if (buf[i] == 0x7f)
|
|
buf[i] = 0x08;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_port_fwd_listen_handler
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_port_fwd_listen_handler(Ip_fd sock, Ipssh_conn_ctx* ctx, Ipssh_map_entry* map_entry)
|
|
{
|
|
Ip_fd clt_sock;
|
|
struct Ip_sockaddr_in clt_addr;
|
|
struct Ip_sockaddr_in srv_addr;
|
|
Ip_u32 len = sizeof(struct Ip_sockaddr_in);
|
|
Ip_u32 clt_port, srv_port;
|
|
Ipssh_channel* channel;
|
|
Ipcom_buffer* pkt;
|
|
|
|
(void) map_entry;
|
|
|
|
clt_sock = ipcom_accept(sock, (struct Ip_sockaddr*)&clt_addr, &len);
|
|
if(clt_sock == IP_INVALID_SOCKET)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_port_fwd_listen_handler :: ipcom_accept() failed, errno=%d", ipcom_errno);
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
len = sizeof(struct Ip_sockaddr_in);
|
|
if(ipcom_getsockname(sock, (struct Ip_sockaddr*)&srv_addr, &len))
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_port_fwd_listen_handler :: ipcom_getsockname() failed, errno=%d", ipcom_errno);
|
|
ipcom_socketclose(clt_sock);
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
channel = ipssh_channel_new(ctx->ssh_conn, 0xffffffff, ipssh_channel2sock_handler);
|
|
if(channel == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_port_fwd_listen_handler :: out of memory");
|
|
ipcom_socketclose(clt_sock);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
channel->data.port_fwd.sock = clt_sock;
|
|
channel->type = IPSSH_CH_TYPE_PORT_FWD;
|
|
srv_port = ip_ntohs(srv_addr.sin_port);
|
|
clt_port = ip_ntohs(clt_addr.sin_port);
|
|
|
|
pkt = ipssh_buffer_new_v2_pkt(128, IPSSH_MSG_CHANNEL_OPEN);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_port_fwd_listen_handler :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
/* Open channel to convey forwarded tcp connection */
|
|
if(ipssh_buffer_put_sshstr(pkt, IPSSH_FORWARDED_TCPIP) ||
|
|
ipcom_buffer_put_int(pkt, ip_htonl(channel->my_ch_id)) ||
|
|
ipcom_buffer_put_int(pkt, ip_htonl(channel->my_win)) ||
|
|
ipcom_buffer_put_int(pkt, ip_htonl(channel->my_max_pkt_size)) ||
|
|
ipssh_buffer_put_sshstr(pkt, ipcom_inet_ntoa(srv_addr.sin_addr)) ||
|
|
ipcom_buffer_put_int(pkt, ip_htonl(srv_port)) ||
|
|
ipssh_buffer_put_sshstr(pkt, ipcom_inet_ntoa(clt_addr.sin_addr)) ||
|
|
ipcom_buffer_put_int(pkt, ip_htonl(clt_port)))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_port_fwd_listen_handler :: failed to pack message");
|
|
ipcom_buffer_free(pkt);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
if(ipssh_send_pkt(ctx->ssh_conn, pkt, IP_TRUE) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_port_fwd_listen_handler :: failed to send message");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_port_fwd_handler_s2c
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_port_fwd_handler_s2c(Ip_fd sock, Ipssh_conn_ctx* ctx, Ipssh_map_entry* map_entry)
|
|
{
|
|
Ipssh_channel* channel;
|
|
Ipcom_buffer* pkt;
|
|
Ip_s32 rc = IPCOM_SUCCESS;
|
|
Ip_u32 req_bytes;
|
|
Ip_ssize_t read_bytes;
|
|
|
|
channel = ipssh_sock2channel(&ctx->ssh_conn->ch_map, sock);
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_port_fwd_handler_s2c :: no channel data for socket: %d", sock);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
if(channel->peer_win == 0)
|
|
{
|
|
/* Wait for a channel window adjust message to arrive */
|
|
IP_FD_CLR(sock, &(ctx->read_set));
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
req_bytes = IP_MIN((Ip_u32)channel->peer_win,channel->peer_max_pkt_size);
|
|
pkt = ipssh_buffer_new_channel_data_pkt(req_bytes, channel);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_port_fwd_handler_s2c :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
read_bytes = ipcom_socketread(sock, ipcom_buffer_end_ptr(pkt), req_bytes);
|
|
if(read_bytes < 0)
|
|
{
|
|
IPCOM_LOG1(INFO, "ipssh_port_fwd_handler_s2c :: connection lost, errno=%d", ipcom_errno);
|
|
}
|
|
if(read_bytes < 0 || /* Port fwd connection lost */
|
|
read_bytes == 0) /* Port fwd connection closed */
|
|
{
|
|
ipcom_list_remove((Ipcom_list*)map_entry);
|
|
ipcom_free(map_entry);
|
|
IP_FD_CLR(sock, &(ctx->read_set));
|
|
if(!channel->close_sent)
|
|
{
|
|
if(ipssh_send_channel_close_msg(channel))
|
|
{
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
if(channel->close_rec)
|
|
ipssh_channel_close(channel);
|
|
if(ctx->ssh_conn->ch_map.size == 0)
|
|
rc = IPCOM_ERR_FAILED; /* Last channel closed, close SSH connection */
|
|
}
|
|
else /* Forward data */
|
|
{
|
|
(void)ipcom_buffer_decr_cap(pkt, read_bytes);
|
|
if(ipssh_send_channel_data_msg(channel, pkt) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_port_fwd_handler_s2c :: ipssh_send_channel_data_msg() failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
if(pkt)
|
|
ipcom_buffer_free(pkt);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_store_char
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_s32
|
|
ipssh_store_char(Ipcom_buffer* buf, Ipssh_term_modes* term_modes, Ip_u8 chr, Ip_u8* last_chr)
|
|
{
|
|
Ip_s32 ret=1;
|
|
|
|
switch(chr)
|
|
{
|
|
case '\n':
|
|
if(!term_modes->ONLCR)
|
|
break;
|
|
if(*last_chr == '\r')
|
|
break;
|
|
(void)ipcom_buffer_put_char(buf, '\r');
|
|
ret++;
|
|
break;
|
|
default:
|
|
break; /* NOP */
|
|
}
|
|
|
|
*last_chr = chr;
|
|
(void)ipcom_buffer_put_char(buf, chr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef IPSSH_ECHO_STDIN
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_store_echo_char
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_s32
|
|
ipssh_store_echo_char(Ipcom_buffer* buf, Ipssh_term_modes* term_modes, Ip_u8 chr, Ip_u8* last_chr)
|
|
{
|
|
Ip_s32 ret=1;
|
|
|
|
(void) term_modes;
|
|
|
|
switch(chr)
|
|
{
|
|
case '\r':
|
|
(void)ipcom_buffer_put_char(buf, '\n');
|
|
break;
|
|
default:
|
|
break; /* NOP */
|
|
}
|
|
|
|
*last_chr = chr;
|
|
(void)ipcom_buffer_put_char(buf, chr);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_parse_shell_return_value
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipssh_parse_shell_return_value(Ipssh_channel* channel, char *data, Ip_ssize_t len)
|
|
{
|
|
int ret_val = 0;
|
|
char *tmp = data;
|
|
char *valuestr = IP_NULL;
|
|
|
|
/* add '\0' to the end of data. */
|
|
tmp[len] = '\0';
|
|
/* search the last match string "value = ". */
|
|
while((tmp = ipcom_strstr(tmp, "value = ")) != IP_NULL)
|
|
{
|
|
valuestr = tmp;
|
|
tmp += ipcom_strlen("value = ");
|
|
}
|
|
|
|
if(valuestr != IP_NULL)
|
|
{
|
|
valuestr += ipcom_strlen("value = ");
|
|
ret_val = ipcom_atoi(valuestr);
|
|
/* re-check whether the found string is output from shell in value = * = 0x*. */
|
|
while(ipcom_isdigit(*valuestr) != 0)
|
|
valuestr++;
|
|
if(ipcom_strncmp(valuestr, " = 0x", ipcom_strlen(" = 0x")) == 0)
|
|
{
|
|
if(ret_val == (int)ipcom_strtol(valuestr + ipcom_strlen(" = 0x"), IP_NULL, 16))
|
|
{
|
|
channel->data.shell.exit_status_val = ret_val;
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_parse_terminal_modes
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipssh_parse_terminal_modes(Ipssh_term_modes* term_modes, Ip_u8* term_mode, Ip_u32 len)
|
|
{
|
|
Ip_u32 pos = 0;
|
|
Ip_u32 value;
|
|
Ip_u8 mode;
|
|
|
|
if(!term_modes)
|
|
return IPCOM_ERR_FAILED;
|
|
|
|
while(pos < len)
|
|
{
|
|
mode = term_mode[pos];
|
|
if (mode == IPSSH_TTY_OP_END)
|
|
{
|
|
IPCOM_LOG2(DEBUG,"ipssh_parse_terminal_modes :: found end mark at pos %d, len=%d", pos, len);
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
pos++;
|
|
if (len - pos < 4)
|
|
{
|
|
IPCOM_LOG1(DEBUG2,"ipssh_parse_terminal_modes :: illegal mode string, len=%d", len);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
value = IPSSH_GET_32_PTR(&(term_mode[pos]));
|
|
IPCOM_LOG2(DEBUG2,"ipssh_parse_terminal_modes :: [%d]:%d", mode, value);
|
|
|
|
switch(mode)
|
|
{
|
|
case IPSSH_ONLCR:
|
|
term_modes->ONLCR = (Ip_u8) value;
|
|
break;
|
|
|
|
case IPSSH_ICRNL:
|
|
term_modes->ICRNL = (Ip_u8) value;
|
|
break;
|
|
|
|
default:
|
|
break; /* NOP */
|
|
}
|
|
pos += 4;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_shell_stdout_data
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC int
|
|
ipssh_send_shell_stdout_data2(Ipssh_conn* ssh_conn, Ip_fd shell_fd, const char* data, Ip_ssize_t len)
|
|
{
|
|
int pos = 0;
|
|
int msg_size;
|
|
Ipssh_channel* channel;
|
|
Ipcom_buffer* pkt = IP_NULL;
|
|
#ifdef IPSSH_USE_V1
|
|
Ip_size_t len_pos = 0;
|
|
#endif
|
|
|
|
IPCOM_LOG2(DEBUG2, "ipssh_send_shell_stdout_data :: shell_fd: %d, len: %d", shell_fd, len);
|
|
|
|
if(!(channel = ipssh_channel_map_lookup(&ssh_conn->ch_map, shell_fd)))
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_send_shell_stdout_data :: no channel data found for shell_fd=%d", shell_fd);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
if(channel->data.shell.shell_type == IPSSH_SHELL_EXEC_CMD)
|
|
{
|
|
/* Firstly try to find a return value in data. */
|
|
if(ipssh_parse_shell_return_value(channel, (char *)data, len) != IPCOM_SUCCESS)
|
|
{
|
|
int index;
|
|
char *tmp = channel->data.shell.return_value;
|
|
Ip_size_t buflen = ipcom_strlen(channel->data.shell.return_value);
|
|
|
|
/* The return value string may locate in two parts. So save the first one. The max string
|
|
is 31(That is value = 4294967295 = 0xFFFFFFFF). If current buflen>32, move the end part
|
|
of buf to the deginning. */
|
|
if(buflen > 31)
|
|
{
|
|
for(index = 31; index < buflen; index++)
|
|
tmp[index - 31] = tmp[index];
|
|
tmp[index - 31] = '\0';
|
|
}
|
|
(void)ipcom_strncat(tmp, data, 31);
|
|
/* try to search again in the buffer. */
|
|
(void)ipssh_parse_shell_return_value(channel, tmp, ipcom_strlen(tmp));
|
|
}
|
|
}
|
|
|
|
while(pos < len)
|
|
{
|
|
|
|
if(ssh_conn->ssh_version == 2)
|
|
{
|
|
msg_size = (int)len+10;
|
|
if(msg_size > channel->peer_win)
|
|
msg_size = channel->peer_win;
|
|
if(msg_size > (int)channel->peer_max_pkt_size)
|
|
msg_size = channel->peer_max_pkt_size;
|
|
if(msg_size <= 2) /* The channel send window is too small to send any more data */
|
|
break;
|
|
|
|
pkt = ipssh_buffer_new_channel_data_pkt(msg_size, channel);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_shell_stdout_data :: out of memory 2");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
}
|
|
#ifdef IPSSH_USE_V1
|
|
else
|
|
{
|
|
pkt = ipssh_buffer_new_v1_pkt(1024, IPSSH_SMSG_V1_STDOUT_DATA);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_shell_stdout_data :: out of memory 1");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
len_pos = ipcom_buffer_len(pkt);
|
|
(void)ipcom_buffer_decr_cap(pkt, 4);
|
|
}
|
|
#endif
|
|
|
|
while(pos < len && ipcom_buffer_capacity(pkt) > (2+IPSSH_MAX_PAD)) /* ipssh_store_char may store 2 characters */
|
|
{
|
|
(void)ipssh_store_char(pkt, &channel->data.shell.term_modes, data[pos], &channel->data.shell.last_chr);
|
|
pos++;
|
|
}
|
|
|
|
if(ssh_conn->ssh_version == 2)
|
|
{
|
|
if(ipssh_send_channel_data_msg(channel, pkt) != IPCOM_SUCCESS)
|
|
{
|
|
ipcom_buffer_free(pkt);
|
|
IPCOM_LOG0(ERR, "ipssh_send_shell_stdout_data :: ipssh_send_channel_data_msg() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
ipcom_buffer_free(pkt);
|
|
}
|
|
#ifdef IPSSH_USE_V1
|
|
else
|
|
{
|
|
int pkt_len;
|
|
char* ptr;
|
|
|
|
pkt_len = (int)(ipcom_buffer_len(pkt) - len_pos - 4);
|
|
ptr = ipcom_buffer_ptr2pos(pkt, len_pos);
|
|
IPSSH_SET_32_PTR(ptr, pkt_len);
|
|
if(ipssh_send_v1_pkt(ssh_conn, pkt) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_shell_stdout_data :: failed to send STDOUT_DATA pkt");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if(pos == len)
|
|
break;
|
|
}
|
|
|
|
if(pos == 0)
|
|
{
|
|
ipcom_errno = IP_ERRNO_EWOULDBLOCK;
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
else
|
|
return pos;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_shell_stdin_handler
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipssh_shell_stdin_handler(Ipssh_channel* channel, Ipcom_buffer* msg)
|
|
{
|
|
Ip_u32 len;
|
|
|
|
if(ipssh_buffer_get_uint(msg, &len) || ipcom_buffer_len(msg) < len)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_shell_stdin_handler :: failed to unpack msg");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
IPCOM_LOG2(DEBUG2, "ipssh_shell_stdin_handler :: ch_id=%d, len=%d", channel->peer_ch_id, len);
|
|
|
|
ipssh_handle_term_modes(ipcom_buffer_start_ptr(msg), len, &(channel->data.shell.term_modes));
|
|
|
|
if(ipssh_sock_shell_stdin(channel->data.shell.shell_fd, (const char*)ipcom_buffer_start_ptr(msg), len) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_shell_stdin_handler :: ipssh.shell_stdin_cb() failed, len=%d", len);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
#ifdef IPSSH_ECHO_STDIN
|
|
{
|
|
Ipcom_buffer* echo_pkt;
|
|
Ip_u8 last_chr = 0;
|
|
Ip_u32 pos = 0;
|
|
|
|
echo_pkt = ipssh_buffer_new_channel_data_pkt(len+1, channel);
|
|
|
|
while(pos < len)
|
|
{
|
|
Ip_u8 chr;
|
|
ipcom_buffer_get_char(msg, &chr);
|
|
(void)ipssh_store_echo_char(echo_pkt, &(channel->data.shell.term_modes), chr, &last_chr);
|
|
pos++;
|
|
if(pos == len
|
|
|| *ipcom_buffer_ptr2pos(msg, ipcom_buffer_len(msg)-1) == '\r'
|
|
|| *ipcom_buffer_ptr2pos(msg, ipcom_buffer_len(msg)-1) == '\n'
|
|
|| ipcom_buffer_capacity(msg)<(IPSSH_MAX_PAD+2))
|
|
{
|
|
if(ipssh_send_channel_data_msg(ctx, channel, echo_pkt) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_shell_stdin_handler :: failed to echo data");
|
|
ipcom_buffer_free(echo_pkt);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
/* Reset buffer for CHANNEL_DATA_MSG re-use */
|
|
echo_pkt->offset = 0;
|
|
echo_pkt->end = 14;
|
|
}
|
|
}
|
|
ipcom_buffer_free(echo_pkt);
|
|
}
|
|
#else
|
|
ipcom_buffer_reset(msg);
|
|
#endif
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_start_shell
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipssh_start_shell(Ipssh_conn* ssh_conn, Ipssh_channel* channel)
|
|
{
|
|
Ip_err rc = IPCOM_SUCCESS;
|
|
|
|
if (ipssh_shell_check_access_cb(ssh_conn->user, 0) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_start_shell :: ipssh_shell_check_access_cb() failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
channel->handler = ipssh_shell_stdin_handler;
|
|
|
|
channel->data.shell.shell_fd = ipssh_sock_shell_start(ssh_conn, ssh_conn->user, ssh_conn->sh_ctx);
|
|
if(channel->data.shell.shell_fd == IP_INVALID_SOCKET)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_start_shell :: ipssh_sock_shell_start() failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
/* No cleaning up needed */
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_set_default_term_modes
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipssh_set_default_term_modes(Ipssh_term_modes* term_modes)
|
|
{
|
|
term_modes->ONLCR = 1;
|
|
#if defined(IP_PORT_VXWORKS) && IPCOM_USE_SHELL == IPCOM_SHELL_NATIVE
|
|
term_modes->ICRNL = 1;
|
|
term_modes->IXBSDEL = 1;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_open_tcp_conn
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_open_tcp_conn(const char* addr_str, Ip_u32 port, Ip_fd* sock)
|
|
{
|
|
union Ip_sockaddr_union addr;
|
|
struct Ip_addrinfo *res;
|
|
Ip_err rc = IPCOM_ERR_FAILED;
|
|
|
|
*sock = IP_INVALID_SOCKET;
|
|
|
|
ipcom_memset(&addr, 0, sizeof(addr));
|
|
|
|
if(ipcom_getaddrinfo(addr_str, IP_NULL, IP_NULL, &res) != 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipssh_open_tcp_conn :: invalid addr: %s", addr_str);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
ipcom_memcpy(&addr.sa, res->ai_addr, res->ai_addrlen);
|
|
addr.sin.sin_port = (Ip_in_port_t)ip_htons(port); /* Port field is at the same offset for both INET and INET6 */
|
|
|
|
*sock = ipcom_socket(res->ai_family, IP_SOCK_STREAM, 0);
|
|
if(*sock == IP_INVALID_SOCKET)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipssh_open_tcp_conn :: ipcom_socket() failed, errno=%d", ipcom_errno);
|
|
goto cleanup;
|
|
}
|
|
if(ipcom_connect(*sock, (struct Ip_sockaddr*)&addr, IPCOM_SA_LEN_GET(&addr)) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipssh_open_tcp_conn :: ipcom_connect() failed, errno=%d", ipcom_errno);
|
|
goto cleanup;
|
|
}
|
|
|
|
rc = IPCOM_SUCCESS;
|
|
|
|
cleanup:
|
|
ipcom_freeaddrinfo(res);
|
|
if(*sock != IP_INVALID_SOCKET && rc != IPCOM_SUCCESS)
|
|
{
|
|
ipcom_socketclose(*sock);
|
|
*sock = IP_INVALID_SOCKET;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_port_fwd_handler_c2s
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_channel2sock_handler(Ipssh_channel* channel, Ipcom_buffer* msg)
|
|
{
|
|
Ip_u32 len;
|
|
Ip_ssize_t rc;
|
|
|
|
if(ipssh_buffer_get_uint(msg, &len) || ipcom_buffer_len(msg) < len)
|
|
{
|
|
IPCOM_LOG0(ERR,"port_fwd_handler_c2s :: failed to unpack msg");
|
|
if(ipssh_send_channel_close_msg(channel) != IPCOM_SUCCESS)
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
IPCOM_LOG2(DEBUG2, "port_fwd_handler_c2s :: ch id: %d, len: %d", channel->peer_ch_id, len);
|
|
|
|
rc = ipcom_socketwrite(channel->data.port_fwd.sock, ipcom_buffer_ptr2pos(msg, 0), len);
|
|
if(rc != (Ip_ssize_t)len)
|
|
{
|
|
IPCOM_LOG1(ERR,"port_fwd_handler_c2s :: ipcom_socket failed, errno=%d", ipcom_errno);
|
|
if(ipssh_send_channel_close_msg(channel) != IPCOM_SUCCESS)
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
(void)ipcom_buffer_consume(msg, len);
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_setup_listen_sock
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_setup_listen_sock(const char* addr_str, Ip_u32 port, Ip_fd* sockp)
|
|
{
|
|
union Ip_sockaddr_union addr;
|
|
struct Ip_addrinfo *res;
|
|
Ip_err rc = IPCOM_SUCCESS;
|
|
Ip_s32 on=1;
|
|
|
|
*sockp = IP_INVALID_SOCKET;
|
|
|
|
ipcom_memset(&addr, 0, sizeof(addr));
|
|
if(ipcom_getaddrinfo(addr_str, IP_NULL, IP_NULL, &res) != 0)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipssh_setup_listen_sock :: invalid addr: %s", addr_str);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
ipcom_memcpy(&addr.sa, res->ai_addr, res->ai_addrlen);
|
|
addr.sin.sin_port = (Ip_in_port_t) ip_htons(port); /* Port field is at the same offset for both INET and INET6 */
|
|
|
|
*sockp = ipcom_socket(res->ai_family, IP_SOCK_STREAM, 0);
|
|
if(*sockp == IP_INVALID_SOCKET)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipssh_setup_listen_sock :: ipcom_socket() failed, errno=%d", ipcom_errno);
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
if(ipcom_setsockopt(*sockp, IP_SOL_SOCKET, IP_SO_REUSEADDR, (void*) &on, sizeof(on)))
|
|
{
|
|
IPCOM_LOG1(INFO, "ipssh_setup_listen_sock :: ipcom_setsockopt(IP_SO_REUSEADDR) failed, error=%d",
|
|
ipcom_errno);
|
|
}
|
|
if(ipcom_bind(*sockp, (struct Ip_sockaddr*)&addr, IPCOM_SA_LEN_GET(&addr.sa)) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG1(ERR,"ipssh_setup_listen_sock :: ipcom_bind() failed, errno=%d", ipcom_errno);
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
if(ipcom_listen(*sockp, 5) != IPCOM_SUCCESS)
|
|
{
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
ipcom_freeaddrinfo(res);
|
|
if(rc != IPCOM_SUCCESS && *sockp != IP_INVALID_SOCKET)
|
|
ipcom_socketclose(*sockp);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_msg_global_request
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_msg_global_request(Ipssh_conn* ssh_conn)
|
|
{
|
|
char* cmd = IP_NULL;
|
|
Ip_u8 want_reply;
|
|
char* addr_str = IP_NULL;
|
|
Ip_u32 port;
|
|
Ip_s32 rc = IPCOM_SUCCESS;
|
|
Ip_fd sock = IP_INVALID_SOCKET;
|
|
|
|
/* Unpack generic part of the request */
|
|
if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6) ||
|
|
!(cmd = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)) ||
|
|
ipcom_buffer_get_char(ssh_conn->recv_plain_buf, &want_reply))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_global_request :: failed to unpack message");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
if(ipcom_strcmp(cmd, IPSSH_TCPIP_FWD) == 0)
|
|
{
|
|
/* Unpack the rest of the message */
|
|
if(!(addr_str = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)) ||
|
|
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &port))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_global_request :: failed to unpack message");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
IPCOM_LOG4(DEBUG, "ipssh_msg_global_request :: cmd=%s, want reply=%d, addr=%s, port=%d", cmd, want_reply, addr_str, port);
|
|
if(!ssh_conn->config.port_fwd)
|
|
{
|
|
IPCOM_LOG0(INFO,"ipssh_msg_global_request :: " IPSSH_TCPIP_FWD ", port forwarding not turned on");
|
|
if(want_reply && ipssh_send_request_failure_msg(ssh_conn) != IPCOM_SUCCESS)
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
if(ipssh_setup_listen_sock(addr_str, port, &sock) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR,"ipssh_msg_global_request :: ipssh_setup_listen_sock() failed");
|
|
if(want_reply && ipssh_send_request_failure_msg(ssh_conn) != IPCOM_SUCCESS)
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
if(want_reply && ipssh_send_request_success_msg(ssh_conn) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_global_request :: ipssh_send_request_success_msg() failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
if(ssh_conn->port_fwd_cb)
|
|
{
|
|
if(ssh_conn->port_fwd_cb(ssh_conn, sock, IPSSH_PORT_FWD_EVENT_LISTEN) != IPCOM_SUCCESS)
|
|
rc = IPCOM_ERR_FAILED;
|
|
else
|
|
/* sock is stored in Ipssh_map_entry. */
|
|
sock = IP_INVALID_SOCKET;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Unknown/unsupported command */
|
|
IPCOM_LOG2(DEBUG, "ipssh_msg_global_request :: unknown cmd=%s, want reply=%d", cmd, want_reply);
|
|
if(want_reply)
|
|
rc = ipssh_send_request_failure_msg(ssh_conn);
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
if(cmd)
|
|
ipcom_free(cmd);
|
|
if(addr_str)
|
|
ipcom_free(addr_str);
|
|
if (sock != IP_INVALID_SOCKET)
|
|
ipcom_socketclose(sock);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_request_success_msg
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_request_success_msg(Ipssh_conn* ssh_conn)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
|
|
pkt = ipssh_buffer_new_v2_pkt(32, IPSSH_MSG_REQUEST_SUCCESS);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_request_success_msg :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
if(ipssh_send_pkt(ssh_conn, pkt, IP_TRUE))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_request_success_msg :: ipssh_send_pkt() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_request_failure_msg
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_request_failure_msg(Ipssh_conn* ssh_conn)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
|
|
pkt = ipssh_buffer_new_v2_pkt(32, IPSSH_MSG_REQUEST_FAILURE);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_request_failure_msg :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
if(ipssh_send_pkt(ssh_conn, pkt, IP_TRUE))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_request_success_msg :: ipssh_send_pkt() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_msg_channel_request
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_msg_channel_request(Ipssh_conn* ssh_conn)
|
|
{
|
|
char* term=IP_NULL;
|
|
Ip_u8* term_modes=IP_NULL;
|
|
Ip_u32 term_mode_len;
|
|
Ip_u32 term_cols;
|
|
Ip_u32 term_rows;
|
|
Ip_u32 term_w_pix;
|
|
Ip_u32 term_h_pix;
|
|
char* cmd = IP_NULL;
|
|
Ip_u8 want_reply;
|
|
Ip_u32 my_ch;
|
|
Ip_s32 rc = IPCOM_SUCCESS;
|
|
char* subsystem = IP_NULL;
|
|
Ipssh_channel* channel;
|
|
char* exec_cmd = IP_NULL;
|
|
|
|
|
|
if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6) ||
|
|
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &my_ch) ||
|
|
IP_NULL == (cmd = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)) ||
|
|
ipcom_buffer_get_char(ssh_conn->recv_plain_buf, &want_reply))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: failed to unpack message");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
IPCOM_LOG3(DEBUG, "ipssh_msg_channel_request :: ch=%d, type=%s, want reply=%d",
|
|
my_ch, cmd, want_reply);
|
|
|
|
channel = ipssh_channel_id2ch(&ssh_conn->ch_map, my_ch);
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_msg_channel_request :: no channel data for id=%d", my_ch);
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
if(ipcom_strcmp(cmd, IPSSH_PTY_REQ) == 0)
|
|
{
|
|
if(IP_NULL == (term = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)) ||
|
|
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &channel->data.shell.init_term_cols) ||
|
|
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &channel->data.shell.init_term_rows) ||
|
|
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &term_w_pix) ||
|
|
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &term_h_pix))
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_msg_channel_request :: failed to unpack pty data, ch=%d",my_ch);
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
ipssh_set_default_term_modes(&channel->data.shell.term_modes);
|
|
term_modes = ipssh_buffer_get_sshbuf(ssh_conn->recv_plain_buf, &term_mode_len);
|
|
if(!ssh_conn->config.term)
|
|
{
|
|
IPCOM_LOG0(INFO, "ipssh_msg_channel_request :: Pseudo terminal requested, but terminal connections not active)");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
if(term_modes && ipssh_parse_terminal_modes(&channel->data.shell.term_modes, term_modes, term_mode_len) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_parse_terminal_modes failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
channel->type = IPSSH_CH_TYPE_SHELL;
|
|
|
|
if(want_reply)
|
|
{
|
|
if(ipssh_send_channel_success(channel))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_success() failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
else if(ipcom_strcmp(cmd, IPSSH_SHELL) == 0)
|
|
{
|
|
if(ipssh_start_shell(ssh_conn, channel) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_start_shell() failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
channel->type = IPSSH_CH_TYPE_SHELL;
|
|
if(want_reply)
|
|
{
|
|
if(ipssh_send_channel_success(channel))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_success() failed 2");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_TERM;
|
|
if (channel->data.shell.init_term_cols != 0 && channel->data.shell.init_term_rows != 0
|
|
&& ipssh_terminal_resize_cb != IP_NULL)
|
|
{
|
|
if (ipssh_terminal_resize_cb(channel->data.shell.shell_fd,
|
|
channel->data.shell.init_term_cols,
|
|
channel->data.shell.init_term_rows) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: terminal_resize_cb failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
else if(ipcom_strcmp(cmd, IPSSH_WIN_CHANGE) == 0)
|
|
{
|
|
if(ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &term_cols)
|
|
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &term_rows)
|
|
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &term_w_pix)
|
|
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &term_h_pix))
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_msg_channel_request :: failed to unpack window change msg, ch=%d",my_ch);
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Ignore if no terminal resize callback is set, or if columns or width is zero */
|
|
if (term_cols != 0 && term_rows != 0 && ipssh_terminal_resize_cb != IP_NULL)
|
|
if (ipssh_terminal_resize_cb(channel->data.shell.shell_fd, term_cols, term_rows) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: win change: terminal_resize_cb failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else if(ipcom_strcmp(cmd, IPSSH_SUBSYSTEM_STR) == 0)
|
|
{
|
|
Ip_fd sock = IP_INVALID_SOCKET;
|
|
|
|
if(!(subsystem = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_buffer_get_sshstr failed for subsystem");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
if (ipssh_channel_init_subsystem(subsystem, channel, &sock) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG1(INFO, "ipssh_msg_channel_request :: ipssh_channel_init_subsystem failed for subsystem %s", subsystem);
|
|
if(want_reply)
|
|
{
|
|
if(ipssh_send_channel_failure(channel))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_failure() failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
if (sock != IP_INVALID_SOCKET)
|
|
{
|
|
int on = 1;
|
|
if (ipcom_socketioctl(sock, IP_FIONBIO, (char *)&on))
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_msg_channel_request :: ipcom_socketioctl() failed on subsystem socket, errno=%d", ipcom_errno);
|
|
goto cleanup;
|
|
}
|
|
|
|
if(!ipssh_store_map_entry(ssh_conn->ssh_conn_ctx, sock, ipssh_port_fwd_handler_s2c, 0))
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_msg_channel_request :: ipssh_store_map_entry() failed", ipcom_errno);
|
|
goto cleanup;
|
|
}
|
|
channel->data.port_fwd.sock = sock;
|
|
channel->handler = ipssh_channel2sock_handler;
|
|
IP_FD_SET(sock, &(channel->ssh_conn->ssh_conn_ctx->read_set));
|
|
}
|
|
if(want_reply)
|
|
{
|
|
if(ipssh_send_channel_success(channel))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_success() failed 3");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
else if(ipcom_strcmp(cmd, IPSSH_X11_REQ) == 0)
|
|
{
|
|
Ip_u8 single_connection;
|
|
char* x11_auth_proto = IP_NULL;
|
|
char* x11_auth_cookie = IP_NULL;
|
|
int x11_screen_no;
|
|
|
|
/* X11 forwarding not supported/needed, Unpack the message and ignore.*/
|
|
if(ipcom_buffer_get_char(ssh_conn->recv_plain_buf, &single_connection)
|
|
|| ((x11_auth_proto = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)) == IP_NULL)
|
|
|| ((x11_auth_cookie = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)) == IP_NULL)
|
|
|| ipcom_buffer_get_int(ssh_conn->recv_plain_buf, (Ip_s32*)&x11_screen_no))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: failed to unpack X11 req");
|
|
rc = IPCOM_ERR_FAILED;
|
|
if (x11_auth_proto)
|
|
ipcom_free(x11_auth_proto);
|
|
if (x11_auth_cookie)
|
|
ipcom_free(x11_auth_cookie);
|
|
goto cleanup;
|
|
}
|
|
ipcom_free(x11_auth_proto);
|
|
ipcom_free(x11_auth_cookie);
|
|
|
|
/*Send channel failure if reply is requested */
|
|
if(want_reply)
|
|
{
|
|
if(ipssh_send_channel_failure(channel))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_failure() failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
else if(ipcom_strcmp(cmd, IPSSH_EXEC) == 0)
|
|
{
|
|
exec_cmd = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf);
|
|
if (!exec_cmd)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: failed to unpack exec command");
|
|
if(want_reply)
|
|
{
|
|
if(ipssh_send_channel_failure(channel))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_failure() failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if(want_reply)
|
|
{
|
|
if(ipssh_send_channel_success(channel))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_success() failed 4");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (ipssh_handle_exec_cmd(ssh_conn, channel, exec_cmd) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_handle_exec_cmd failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else if(ipcom_strcmp(cmd, IPSSH_EXIT_STATUS) == 0)
|
|
{
|
|
if(want_reply)
|
|
{
|
|
if(ipssh_send_channel_success(channel))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_success() failed 4");
|
|
}
|
|
}
|
|
rc = IPCOM_ERR_PERMISSION_DENIED;
|
|
goto cleanup;
|
|
}
|
|
else
|
|
{
|
|
/* Unsupported request received. Send channel failure if reply is requested. */
|
|
IPCOM_LOG2(DEBUG, "ipssh_msg_channel_request :: unknown type: %s, want reply=%d. Ignoring", cmd, want_reply);
|
|
if(want_reply)
|
|
{
|
|
if(ipssh_send_channel_failure(channel))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_failure() failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
if(cmd)
|
|
ipcom_free(cmd);
|
|
if(term)
|
|
ipcom_free(term);
|
|
if(term_modes)
|
|
ipcom_free(term_modes);
|
|
if(subsystem)
|
|
ipcom_free(subsystem);
|
|
if(exec_cmd)
|
|
ipcom_free(exec_cmd);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_shell_exit
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_shell_exit2(Ipssh_conn* ssh_conn, Ip_fd shell_fd)
|
|
{
|
|
Ipssh_channel* channel;
|
|
Ip_err rc;
|
|
|
|
channel = ipssh_channel_map_lookup(&ssh_conn->ch_map, shell_fd);
|
|
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_shell_exit :: failed to lookup channel for shell_fd %d", shell_fd);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
#ifdef IPSSH_USE_V1
|
|
if(ssh_conn->ssh_version == 1)
|
|
{
|
|
rc = ipssh_send_msg_v1_exit_status(ssh_conn, 0);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
(void)ipssh_send_channel_exit(channel);
|
|
rc = ipssh_send_channel_close_msg(channel);
|
|
}
|
|
|
|
|
|
/* If this is the last channel, make sure the connection will be shutdown. */
|
|
if(ssh_conn->ch_map.size == 1)
|
|
{
|
|
/* The ssh server is hanging in a select call. Close the socket to release the daemon, who will do the cleanup */
|
|
ipcom_socketclose(ssh_conn->sock);
|
|
ssh_conn->sock = IP_INVALID_SOCKET;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_set_terminal_resize_cb
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC void
|
|
ipssh_set_terminal_resize_cb(Ipssh_terminal_resize_cb terminal_resize_cb)
|
|
{
|
|
ipssh_terminal_resize_cb = terminal_resize_cb;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|