/* ****************************************************************************** * 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 ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include /* **************************************************************************** * 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 #include #include #include #include #include #include #include #include #include /* **************************************************************************** * 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; iICRNL) 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 **************************************************************************** */