/* ****************************************************************************** * SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_RCPL8_2024030303 $ $RCSfile: ipssh.c,v $ $Revision: 1.141.10.31 $ * $Source: /home/interpeak/CVSRoot/ipssh/src/ipssh.c,v $ * $Author: yli10 $ $Date: 2020-06-08 06:42:36 $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Roger Boden ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include "ipssh.h" #include "ipssh_h.h" #include "ipssh_config.h" #include #include "ipssh_transport.h" #ifdef IPSSH_USE_V1 #include "ipssh_transport_v1.h" #include "ipssh_connection_v1.h" #endif #include "ipssh_connection.h" #include "ipssh_sftp.h" #include "ipssh_list.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* **************************************************************************** * 4 EXTERN PROTOTYPES **************************************************************************** */ IP_EXTERN void ipssh_term_data_free(void * term_data); #ifdef IP_PORT_OSE5 extern int efs_donate_fd(int fd, Ip_pid_t to); extern int efs_receive_fd(Ip_pid_t from); #endif #ifdef IP_PORT_VXWORKS extern Ip_bool systemSecurityIsEnabled; #include #endif /* **************************************************************************** * 5 DEFINES **************************************************************************** */ /* **************************************************************************** * 6 TYPES **************************************************************************** */ typedef struct Ipssh_config_map_st { const char *sysvar; const char *str; Ip_u8 id; int flags; } Ipssh_config_map; /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ IP_STATIC Ipssh_conn_ctx *ipssh_conn_ctx_new(void); IP_STATIC void ipssh_conn_ctx_free(Ipssh_conn_ctx *ctx); IP_STATIC void ipssh_conn_ctx_set_srv_ctx(Ipssh_conn_ctx * ctx, Ipssh_srv_ctx *srv_ctx); IP_STATIC void ipssh_close_conn(Ipssh_conn_ctx *ctx); IP_STATIC Ip_err ipssh_activate_v2_alg(Ip_u8 *alg_array, Ip_size_t len, int alg_type); IP_GLOBAL Ip_err ipssh_read_version_str(Ip_fd sock, Ipssh_conn_ctx *ctx, Ipssh_map_entry *map_entry); IP_GLOBAL void ipssh_launch_server(Ip_fd sock, Ipssh_srv_ctx *srv_ctx); IP_GLOBAL Ip_u8 ipssh_alg_str2id(const char *str); IPCOM_PROCESS(ipssh_server); IP_GLOBAL void ipssh_register_clt(Ipssh_conn_ctx *ctx); IP_GLOBAL void ipssh_unregister_clt(Ipssh_conn_ctx *ctx); IP_GLOBAL Ipssh_map_entry *ipssh_lookup_map_entry(Ipssh_ctx_map *map, Ip_fd sock); IP_GLOBAL void ipssh_delete_map_entry(Ipssh_map_entry *entry); IP_GLOBAL void ipssh_delete_map_entry_for_sock(Ipssh_ctx_map *map, Ip_fd sock); IP_GLOBAL Ip_fd ipssh_map_get_max_sock(Ipssh_ctx_map* map); IPCOM_PROCESS(ipssh); IP_GLOBAL Ip_err ipssh_port_fwd_cb(struct Ipssh_conn_st *ssh_conn, Ip_fd port_fwd_sock, int event); IP_PUBLIC Ip_err ipssh_start(void); IP_PUBLIC Ip_err ipssh_create(void); IP_GLOBAL Ip_err ipssh_port_fwd_cb_v1(Ipssh_conn_ctx *ctx, Ip_fd port_fwd_sock, int event); IP_GLOBAL Ip_err ipssh_port_fwd_cb_v2(Ipssh_conn_ctx *ctx, Ip_fd port_fwd_sock, int event); /* **************************************************************************** * 8 DATA **************************************************************************** */ IP_STATIC Ipssh_config_map ipssh_alg_ids[] = { /* Encryption algorithms */ #ifdef IPSSH_USE_AES {"ipssh.enc.aes", IPSSH_ENC_AES128_CBC_STR, IPSSH_ENC_AES128_CBC_ID, IPSSH_FLAG_ALG_ENC}, {"ipssh.enc.aes192", IPSSH_ENC_AES192_CBC_STR, IPSSH_ENC_AES192_CBC_ID, IPSSH_FLAG_ALG_ENC}, {"ipssh.enc.aes256", IPSSH_ENC_AES256_CBC_STR, IPSSH_ENC_AES256_CBC_ID, IPSSH_FLAG_ALG_ENC}, #endif #ifdef IPSSH_USE_BLOWFISH {"ipssh.enc.blowfish", IPSSH_ENC_BLOWFISH_CBC_STR, IPSSH_ENC_BLOWFISH_CBC_ID, IPSSH_FLAG_ALG_ENC}, #endif #ifdef IPSSH_USE_CAST {"ipssh.enc.cast", IPSSH_ENC_CAST128_CBC_STR, IPSSH_ENC_CAST128_CBC_ID, IPSSH_FLAG_ALG_ENC}, #endif #ifdef IPSSH_USE_DES {"ipssh.enc.3des", IPSSH_ENC_3DES_CBC_STR, IPSSH_ENC_3DES_CBC_ID, IPSSH_FLAG_ALG_ENC}, {"ipssh.enc.des", IPSSH_ENC_DES_CBC_STR, IPSSH_ENC_DES_CBC_ID, IPSSH_FLAG_ALG_ENC}, {"ipssh.enc.des", IPSSH_ENC_DES_CBC_STR "@ssh.com", IPSSH_ENC_DES_CBC_ID, IPSSH_FLAG_ALG_ENC}, #endif #ifdef IPSSH_USE_ARCFOUR {"ipssh.enc.arcfour128", IPSSH_ENC_ARCFOUR128_STR, IPSSH_ENC_ARCFOUR_128_ID, IPSSH_FLAG_ALG_ENC}, {"ipssh.enc.arcfour", IPSSH_ENC_ARCFOUR_STR, IPSSH_ENC_ARCFOUR_ID, IPSSH_FLAG_ALG_ENC}, #endif {"ipssh.enc.none", IPSSH_NONE_STR, IPSSH_ENC_NONE_ID, IPSSH_FLAG_ALG_ENC}, #ifdef _WRS_CONFIG_COMPONENT_IPSSH_SUPPORT_SHA2 /* MAC algorithms */ #ifdef IPSSH_USE_SHA256 {"ipssh.mac.sha256", IPSSH_MAC_SHA256_STR, IPSSH_MAC_SHA256_ID, IPSSH_FLAG_ALG_MAC}, #endif #ifdef IPSSH_USE_SHA512 {"ipssh.mac.sha512", IPSSH_MAC_SHA512_STR, IPSSH_MAC_SHA512_ID, IPSSH_FLAG_ALG_MAC}, #endif #endif #ifdef IPSSH_USE_SHA1 {"ipssh.mac.sha1", IPSSH_MAC_SHA1_STR, IPSSH_MAC_SHA1_ID, IPSSH_FLAG_ALG_MAC}, {"ipssh.mac.sha1-96", IPSSH_MAC_SHA1_96_STR, IPSSH_MAC_SHA1_96_ID, IPSSH_FLAG_ALG_MAC}, #endif #ifdef IPSSH_USE_MD5 {"ipssh.mac.md5", IPSSH_MAC_MD5_STR, IPSSH_MAC_MD5_ID, IPSSH_FLAG_ALG_MAC|IPSSH_FLAG_ALG_NOT_ALLOWED_IN_FIPS_MODE}, {"ipssh.mac.md5-96", IPSSH_MAC_MD5_96_STR, IPSSH_MAC_MD5_96_ID, IPSSH_FLAG_ALG_MAC|IPSSH_FLAG_ALG_NOT_ALLOWED_IN_FIPS_MODE}, #endif {"ipssh.mac.none", IPSSH_MAC_NONE_STR, IPSSH_MAC_NONE_ID, IPSSH_FLAG_ALG_MAC}, /* key exchange algs */ {"ipssh.kex.rsa2048-sha256", IPSSH_KEYEX_RSA2048_SHA256_STR, IPSSH_KEYEX_RSA2048_SHA256_ID, IPSSH_FLAG_ALG_KEX}, {"ipssh.kex.rsa1024-sha1", IPSSH_KEYEX_RSA1024_SHA1_STR, IPSSH_KEYEX_RSA1024_SHA1_ID, IPSSH_FLAG_ALG_KEX}, {"ipssh.kex.dh_gr14_sha1", IPSSH_KEYEX_DH_GR14_SHA1_STR, IPSSH_KEYEX_DH_GR14_SHA1_ID, IPSSH_FLAG_ALG_KEX}, {"ipssh.kex.dh_gr1_sha1", IPSSH_KEYEX_DH_GR1_SHA1_STR, IPSSH_KEYEX_DH_GR1_SHA1_ID, IPSSH_FLAG_ALG_KEX}, #ifdef IPSSH_USE_DH_GROUP_EX {"ipssh.kex.dh_gex.sha1", IPSSH_KEYEX_DH_GEX_SHA1_STR, IPSSH_KEYEX_DH_GEX_SHA1_ID, IPSSH_FLAG_ALG_KEX}, {"ipssh.kex.dh_gex.sha256", IPSSH_KEYEX_DH_GEX_SHA256_STR, IPSSH_KEYEX_DH_GEX_SHA256_ID, IPSSH_FLAG_ALG_KEX}, #endif /* Host key algs */ #ifdef IPSSH_USE_RSA {"ipssh.host_key.rsa", IPSSH_RSA_STR, IPSSH_RSA_ID, IPSSH_FLAG_ALG_SRV_KEY}, #endif {"ipssh.host_key.dsa", IPSSH_DSA_STR, IPSSH_DSA_ID, IPSSH_FLAG_ALG_SRV_KEY}, /* Compression algs */ {IP_NULL, IPSSH_NONE_STR, IPSSH_COMPR_NONE_ID, IPSSH_FLAG_ALG_COMPR}, /* End marker */ {IP_NULL, IP_NULL, 0} }; IP_GLOBAL Ipssh_data g_ipssh; /* **************************************************************************** * 9 LOCAL FUNCTIONS **************************************************************************** */ IP_STATIC Ip_err ipssh_sysvar_get_as_int(const char *name, int *value, int default_value) { *value = ipcom_sysvar_get_as_int(name, default_value); if (*value == -1) return IPCOM_ERR_FAILED; return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_sysvar_get_as_u8 *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipssh_sysvar_get_as_u8(const char *name, Ip_u8 *value) { int tmp; tmp = ipcom_sysvar_get_as_int(name, -1); if (tmp == -1) { IPCOM_LOG1(ERR, "ipssh_get_sysvar_get_as_int :: failed to get \"%s\"", name); return IPCOM_ERR_FAILED; } *value = (Ip_u8) tmp; return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_get_banner_config *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_get_banner_config(Ipssh_conn_config* config) { Ip_err ret_val = IPCOM_SUCCESS; Ip_size_t len = 0; char *auth_banner = IP_NULL; auth_banner = ipcom_sysvar_get("ipssh.auth.banner", IP_NULL, IP_NULL); if (auth_banner) { #ifdef IPSSH_SUPPORT_LONG_BANNER config->auth_banner = auth_banner; #else strncpy(config->auth_banner, auth_banner, IPSSH_AUTH_BANNER_MAX_LEN); config->auth_banner[IPSSH_AUTH_BANNER_MAX_LEN-1] = 0x0; ipcom_free(auth_banner); #endif } #ifdef IP_PORT_VXWORKS else { if(_func_loginBanner != IP_NULL) { if (_func_loginBanner(&auth_banner, (int *)&len) > 0) { #ifdef IPSSH_SUPPORT_LONG_BANNER config->auth_banner = ipcom_strdup(auth_banner); if(!config->auth_banner) { IPCOM_LOG0(ERR, "No enough memory for strdup"); ret_val = IPCOM_ERR_FAILED; } #else strncpy(config->auth_banner, auth_banner, IPSSH_AUTH_BANNER_MAX_LEN); if (len >= IPSSH_AUTH_BANNER_MAX_LEN) config->auth_banner[IPSSH_AUTH_BANNER_MAX_LEN-1] = 0x0; else config->auth_banner[len] = 0x0; #endif } } } #endif return ret_val; } /* *=========================================================================== * ipssh_get_config *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_get_config(Ipssh_conn_config* config) { Ip_err ret_val = IPCOM_SUCCESS; Ip_size_t len = 0; if (ipssh_sysvar_get_as_int("ipssh.max_channels", &config->max_channels, 10) != IPCOM_SUCCESS) { if (ipssh_sysvar_get_as_int("ipssh.max_shells", &config->max_channels, 10) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; } if (ipssh_sysvar_get_as_u8("ipssh.service.terminal", &(config->term)) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; #ifdef IPSSH_USE_SFTP if (ipssh_sysvar_get_as_u8("ipssh.service.sftp", &config->sftp) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; #endif if (ipssh_sysvar_get_as_u8("ipssh.service.port_fwd", &config->port_fwd) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; #ifdef IP_PORT_VXWORKS if (!systemSecurityIsEnabled) { config->auth_none_allowed = 1; } else #endif { config->auth_none_allowed = ipcom_sysvar_get_as_int("ipssh.auth.none.allowed", 0); if (ipssh_sysvar_get_as_u8("ipssh.auth.pw.allowed", &config->pw_allowed) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_sysvar_get_as_u8("ipssh.auth.pw.required", &config->pw_required) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_sysvar_get_as_u8("ipssh.auth.pub_key.allowed", &config->pub_key_allowed) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_sysvar_get_as_u8("ipssh.auth.pub_key.required", &config->pub_key_required) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_sysvar_get_as_u8("ipssh.auth.pub_key_first", &config->pub_key_first) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_sysvar_get_as_u8("ipssh.auth.hostbased.allowed", &config->hostbased_allowed) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_sysvar_get_as_u8("ipssh.auth.hostbased.required", &config->hostbased_required) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_sysvar_get_as_int("ipssh.auth.max_fail", &config->max_fail, 3) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; } if (ipssh_sysvar_get_as_int("ipssh.kex.dh_gex.clt.min", &config->gex_min, 2048) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_sysvar_get_as_int("ipssh.kex.dh_gex.clt.n", &config->gex_n, 2048) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_sysvar_get_as_int("ipssh.kex.dh_gex.clt.max", &config->gex_max, 4096) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_activate_v2_alg(config->v2_algs.kex_alg, sizeof(config->v2_algs.kex_alg), IPSSH_FLAG_ALG_KEX) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_activate_v2_alg(config->v2_algs.srv_host_key_alg, sizeof(config->v2_algs.srv_host_key_alg), IPSSH_FLAG_ALG_SRV_KEY) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_activate_v2_alg(config->v2_algs.enc_alg_clt2srv, sizeof(config->v2_algs.enc_alg_clt2srv), IPSSH_FLAG_ALG_ENC) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_activate_v2_alg(config->v2_algs.enc_alg_srv2clt, sizeof(config->v2_algs.enc_alg_srv2clt), IPSSH_FLAG_ALG_ENC) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_activate_v2_alg(config->v2_algs.mac_alg_clt2srv, sizeof(config->v2_algs.mac_alg_clt2srv), IPSSH_FLAG_ALG_MAC) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_activate_v2_alg(config->v2_algs.mac_alg_srv2clt, sizeof(config->v2_algs.mac_alg_srv2clt), IPSSH_FLAG_ALG_MAC) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_activate_v2_alg(config->v2_algs.compr_alg_clt2srv, sizeof(config->v2_algs.compr_alg_clt2srv), IPSSH_FLAG_ALG_COMPR) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_activate_v2_alg(config->v2_algs.compr_alg_srv2clt, sizeof(config->v2_algs.compr_alg_srv2clt), IPSSH_FLAG_ALG_COMPR) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; #ifdef IPSSH_USE_V1 if (ipssh_sysvar_get_as_u8("ipssh.v1", &config->v1) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_sysvar_get_as_u8("ipssh.v2", &config->v2) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipcom_sysvar_get_as_int("ipssh.enc.3des", -1) == 1) config->v1_enc_algs |= 1 << IPSSH_V1_CIPHER_3DES; if (ipcom_sysvar_get_as_int("ipssh.enc.des", -1) == 1) config->v1_enc_algs |= 1 << IPSSH_V1_CIPHER_DES; if (ipcom_sysvar_get_as_int("ipssh.auth.pw.allowed", -1) == 1) config->v1_auth_meth |= 1 << IPSSH_V1_PW_AUTH; if (ipcom_sysvar_get_as_int("ipssh.auth.pub_key.allowed", -1) == 1) config->v1_auth_meth |= 1 << IPSSH_V1_RSA_AUTH; #else config->v2 = 1; #endif ret_val = ipssh_get_banner_config(config); len = sizeof(config->sftp_start_dir); if (!ipcom_sysvar_get("ipssh.sftp.start_dir", config->sftp_start_dir, &len)) if (len > sizeof(config->sftp_start_dir)) { IPCOM_LOG0(ERR, "Configured sftp start directory is too long"); ret_val = IPCOM_ERR_FAILED; } return ret_val; } /* *=========================================================================== * ipssh_get_srv_config *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipssh_get_srv_config(Ipssh_srv_ctx *srv_ctx) { Ip_err ret_val = IPCOM_SUCCESS; char buf[16]; int i; IPCOM_LOG0(DEBUG2, "ipssh :: getting server config"); if (ipssh_get_config(&srv_ctx->config) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_sysvar_get_as_int("ipssh.port", &srv_ctx->port[0], 22) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; for (i=1; iport[i], -1) != IPCOM_SUCCESS) { srv_ctx->port[i] = 0; break; } } if (ipssh_sysvar_get_as_int("ipssh.idle_timeout", &srv_ctx->idle_timeout, 0) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipssh_sysvar_get_as_int("ipssh.max_clients", &srv_ctx->max_clients, 20) != IPCOM_SUCCESS) ret_val = IPCOM_ERR_FAILED; if (ipcom_sysvar_get_as_int("ipssh.host_key.dsa", 1) != 0) { IPCOM_LOG0(DEBUG2, "ipssh :: loading dsa key"); srv_ctx->dsa_key = ipssh_load_dsa_key_cb(); if (srv_ctx->dsa_key == IP_NULL) { IPCOM_LOG0(ERR, "ipssh_get_srv_config :: ipssh_load_dsa_key_cb() failed"); return IPCOM_ERR_FAILED; } } #ifdef IPSSH_USE_RSA if (ipcom_sysvar_get_as_int("ipssh.host_key.rsa", 1) != 0) { IPCOM_LOG0(DEBUG2, "ipssh :: loading rsa key"); srv_ctx->rsa_key = ipssh_load_rsa_key_cb(); if (srv_ctx->rsa_key == IP_NULL) { IPCOM_LOG0(ERR, "ipssh_get_srv_config :: ipssh_load_rsa_key_cb() failed"); return IPCOM_ERR_FAILED; } /* Check that the RSA key is long enough */ if (RSA_size(srv_ctx->rsa_key)*8 < 768) { IPCOM_LOG0(ERR, "Configured RSA key too short. Must be at least 768 bits."); return IPCOM_ERR_FAILED; } } if (ipcom_sysvar_get_as_int("ipssh.kex.rsa1024-sha1", 0) != 0 || ipcom_sysvar_get_as_int("ipssh.kex.rsa2048-sha256", 0) != 0) { srv_ctx->rsa_keyex_key = ipssh_load_rsa_keyex_key_cb(); if (srv_ctx->rsa_keyex_key == IP_NULL) { IPCOM_LOG0(ERR, "ipssh_get_srv_config :: ipssh_load_rsa_keyex_key_cb() failed"); return IPCOM_ERR_FAILED; } /* Check that the RSA key exchange key is long enough */ if (ipcom_sysvar_get_as_int("ipssh.kex.rsa1024-sha1", 0) != 0 && RSA_size(srv_ctx->rsa_keyex_key)*8 < 1024 ) { IPCOM_LOG0(ERR, "Configured RSA key exchange key too short. Must be at least 1024 bits in order to enable rsa1024-sha1."); return IPCOM_ERR_FAILED; } if (ipcom_sysvar_get_as_int("ipssh.kex.rsa2048-sha256", 0) != 0 && RSA_size(srv_ctx->rsa_keyex_key)*8 < 2048 ) { IPCOM_LOG0(ERR, "Configured RSA key exchange key too short. Must be at least 2048 bits in order to enable rsa2048-sha256."); return IPCOM_ERR_FAILED; } } #endif return ret_val; } /* *=========================================================================== * ipssh_activate_v2_alg *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipssh_activate_v2_alg(Ip_u8 *alg_array, Ip_size_t len, int alg_type) { Ipssh_config_map *alg_ids = ipssh_alg_ids; Ip_size_t pos = 0; #ifdef OPENSSL_FIPS int fips_mode = FIPS_mode(); #endif for (alg_ids = ipssh_alg_ids; alg_ids->str; alg_ids++) { ip_assert(posflags & alg_type) { /* Check if this algorithm is possible to turn off in runtime */ if (alg_ids->sysvar) { int value; if (ipssh_sysvar_get_as_int(alg_ids->sysvar, &value, -1) != IPCOM_SUCCESS) { IPCOM_LOG1(WARNING, "ipssh_activate_v2_alg :: failed to get sysvar: %s", alg_ids->sysvar); continue; /* Sysvar not set, treat as algorithm not turned on. */ } if (!value) continue; /* This alg is not turned on, continue */ } #ifdef OPENSSL_FIPS /* Check if this algorithm is allowed in FIPS mode */ if (fips_mode && alg_ids->flags & IPSSH_FLAG_ALG_NOT_ALLOWED_IN_FIPS_MODE) { IPCOM_LOG1(INFO, "ipssh_activate_v2_alg :: %s not allowed in FIPS mode", alg_ids->str); continue; } #endif alg_array[pos] = alg_ids->id; pos++; } } return IPCOM_SUCCESS; } #ifdef IPSSH_USE_STOP_SPAWN /* *=========================================================================== * ipssh_connect_to_port *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipssh_connect_to_port(Ip_u16 port) { struct Ip_sockaddr_in addr; Ip_fd sock; ipcom_memset(&addr, 0, sizeof(addr)); IPCOM_SA_LEN_SET(&addr, sizeof(struct Ip_sockaddr_in)); addr.sin_addr.s_addr = ip_htonl(IP_INADDR_LOOPBACK); addr.sin_port = port; addr.sin_family = IP_AF_INET; sock = ipcom_socket(IP_AF_INET, IP_SOCK_STREAM, 0); if (sock == IP_INVALID_SOCKET) return IPCOM_ERR_SOCKCALLFAILED; if (ipcom_connect(sock, (struct Ip_sockaddr*) &addr, sizeof(addr)) != IPCOM_SUCCESS) { ipcom_socketclose(sock); return IPCOM_ERR_SOCKCALLFAILED; } ipcom_socketclose(sock); return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_setup_stop_sock *=========================================================================== * Description: Sets up a listen socket, bound to an arbitrary port. * Parameters: * Returns: The socket descriptor and the port number. * */ IP_STATIC Ip_err ipssh_setup_stop_sock(Ip_fd *sock, Ip_u16 *port) { struct Ip_sockaddr_in addr; Ip_socklen_t addr_len; int opt_val = 1; *sock = ipcom_socket(IP_AF_INET, IP_SOCK_STREAM, 0); if (*sock == IP_INVALID_SOCKET) { IPCOM_LOG1(ERR, "ipssh_setup_stop_sock :: ipcom_socket() failed, errno=%d", ipcom_errno); return IPCOM_ERR_FAILED; } if (ipcom_setsockopt(*sock, IP_SOL_SOCKET, IP_SO_REUSEADDR, (void *) &opt_val, sizeof(opt_val))) { IPCOM_LOG1(INFO,"ipssh_setup_stop_sock :: ipcom_setsockopt(IP_SO_REUSEADDR) failed, error=%d", ipcom_errno); } ipcom_memset(&addr, 0, sizeof(addr)); addr.sin_addr.s_addr = ip_htonl(IP_INADDR_LOOPBACK); addr.sin_family = IP_AF_INET; addr.sin_len = sizeof(struct Ip_sockaddr_in); addr_len = sizeof(addr); if (ipcom_bind(*sock, (struct Ip_sockaddr *) &addr, addr_len) != IPCOM_SUCCESS) { IPCOM_LOG1(ERR, "ipssh_setup_stop_sock :: ipcom_bind() failed, errno=%d", ipcom_errno); ipcom_socketclose(*sock); return IPCOM_ERR_FAILED; } addr_len = sizeof(addr); if (ipcom_getsockname(*sock, (struct Ip_sockaddr *) &addr, &addr_len) != IPCOM_SUCCESS) { IPCOM_LOG1(ERR, "ipssh_setup_stop_sock :: ipcom_getsockname() failed, errno=%d", ipcom_errno); return IPCOM_ERR_FAILED; } *port = addr.sin_port; if (ipcom_listen(*sock, 2) != IPCOM_SUCCESS) { IPCOM_LOG1(ERR, "ipssh_setup_stop_sock :: ipcom_listen() failed, errno=%d", ipcom_errno); return IPCOM_ERR_FAILED; } return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_stop_spawned_server *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_err ipssh_stop_spawned_server(Ip_fd sock, Ipssh_conn_ctx *ctx, Ipssh_map_entry *map_entry) { /* Force the connection to be disconnected. */ (void) sock; (void) ctx; (void) map_entry; return IPCOM_ERR_FAILED; } #endif /* IPSSH_USE_STOP_SPAWN */ /* *=========================================================================== * ipssh_initiate_v1_connection *=========================================================================== * Description: Read client's version string * Parameters: * Returns: * */ #ifdef IPSSH_USE_V1 IP_STATIC Ip_err ipssh_initiate_v1_connection(Ipssh_conn *ssh_conn, Ipssh_map_entry *map_entry) { /* initiate v1 key exchange */ ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_KEY_EX_V1; ssh_conn->server_key = ipssh_generate_server_key(ssh_conn->srv_rsa_key); if (!ssh_conn->server_key) { IPCOM_LOG0(ERR, "ipssh_initiate_v1_connection :: ipssh_generate_server_key() failed"); return IPCOM_ERR_FAILED; } if (ipssh_send_msg_v1_public_key(ssh_conn) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_initiate_v1_connection :: ipssh_send_msg_v1_public_key() failed"); return IPCOM_ERR_FAILED; } map_entry->handler = ipssh_read_v1_pkt; return IPCOM_SUCCESS; } #endif /* IPSSH_USE_V1 */ /* *=========================================================================== * ipssh_initiate_v2_connection *=========================================================================== * Description: Read client's version string * Parameters: * Returns: * */ IP_STATIC Ip_err ipssh_initiate_v2_connection(Ipssh_conn *ssh_conn, Ipssh_map_entry *map_entry) { if (ipssh_send_kexinit(ssh_conn)) return IPCOM_ERR_FAILED; map_entry->handler = ipssh_conn_v2_handler; return IPCOM_SUCCESS; } /* **************************************************************************** * 10 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipssh_srv_ctx_new *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ipssh_srv_ctx* ipssh_srv_ctx_new(void) { Ipssh_srv_ctx* srv_ctx; srv_ctx = ipcom_calloc(1, sizeof(Ipssh_srv_ctx)); if (!srv_ctx) return IP_NULL; srv_ctx->ref = 1; #ifdef IPSSH_SUPPORT_LONG_BANNER srv_ctx->config.auth_banner = IP_NULL; #endif return srv_ctx; } /* *=========================================================================== * ipssh_srv_ctx_free *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL void ipssh_srv_ctx_free(Ipssh_srv_ctx* srv_ctx) { ip_assert(srv_ctx->ref > 0); srv_ctx->ref--; if (srv_ctx->ref > 0) return; #ifdef IPSSH_USE_RSA if (srv_ctx->rsa_key) RSA_free(srv_ctx->rsa_key); if (srv_ctx->rsa_keyex_key) RSA_free(srv_ctx->rsa_keyex_key); #endif if (srv_ctx->dsa_key) DSA_free(srv_ctx->dsa_key); #ifdef IPSSH_SUPPORT_LONG_BANNER if(srv_ctx->config.auth_banner) ipcom_free(srv_ctx->config.auth_banner); #endif ipcom_free(srv_ctx); } /* *=========================================================================== * ipssh_srv_ctx_dup *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ipssh_srv_ctx* ipssh_srv_ctx_dup(Ipssh_srv_ctx* srv_ctx) { srv_ctx->ref++; return srv_ctx; } /* *=========================================================================== * ipssh_alg_list_str2id *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_alg_list_str2id(const char *str_list_org, Ip_u8 *id_list, int id_list_len) { Ip_err rc = IPCOM_ERR_FAILED; char *str_list; char *alg; char *last; Ip_u8 id; str_list = ipcom_strdup(str_list_org); for (alg = ipcom_strtok_r(str_list, ",", &last); alg; alg = ipcom_strtok_r(IP_NULL, ",", &last)) { id = ipssh_alg_str2id(alg); if (id != IPSSH_ID_UNKNOWN) { id_list_len--; ip_assert(id_list_len > 0); if (id_list_len < 0) goto cleanup; *id_list = id; id_list++; } else { IPCOM_LOG1(DEBUG2, "ipssh_alg_list_str2id :: unknown alg: %s", alg); } } rc = IPCOM_SUCCESS; cleanup: ipcom_free(str_list); return rc; } /* *=========================================================================== * ipssh_register_clt *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL void ipssh_register_clt(Ipssh_conn_ctx *ctx) { ipcom_list_insert_last(&(g_ipssh.clt_list), (Ipcom_list*) ctx); } /* *=========================================================================== * ipssh_unregister_clt *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL void ipssh_unregister_clt(Ipssh_conn_ctx *ctx) { IPSSH_CODE_LOCK(); ipcom_list_remove((Ipcom_list*) ctx); IPSSH_CODE_UNLOCK(); } /* *=========================================================================== * ipssh_get_no_clients *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL int ipssh_get_no_clients(void) { IPSSH_CODE_ISLOCKED_ASSERT(); return g_ipssh.clt_list.size; } /* *=========================================================================== * ipssh_lookup_map_entry *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ipssh_map_entry* ipssh_lookup_map_entry(Ipssh_ctx_map *map, Ip_fd sock) { Ipcom_list *node; for (node = ipcom_list_first(map); node != IP_NULL; node = ipcom_list_next(node)) { Ipssh_map_entry *entry = (Ipssh_map_entry*) node; if (entry->sock == sock) return entry; } return IP_NULL; } /* *=========================================================================== * ipssh_delete_map_entry *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL void ipssh_delete_map_entry_for_sock(Ipssh_ctx_map *map, Ip_fd sock) { Ipssh_map_entry *entry; entry = ipssh_lookup_map_entry(map, sock); if (entry) { ipcom_list_remove((Ipcom_list*)entry); ipcom_free(entry); } } #ifdef IPSSH_USE_STOP_SPAWN /* *=========================================================================== * ipssh_stop_spawn *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipssh_stop_spawn(Ip_pid_t pid) { Ip_u16 port = 0; Ipssh_conn_ctx *ctx; Ip_bool found = IP_FALSE; IPSSH_CODE_LOCK(); for (ctx = (Ipssh_conn_ctx*) ipcom_list_first(&(g_ipssh.clt_list)); ctx; ctx = (Ipssh_conn_ctx*) ipcom_list_next(&(ctx->list))) { if (ctx->pid == pid) { port = ctx->stop_port; found = IP_TRUE; break; } } IPSSH_CODE_UNLOCK(); if (!found) return IPCOM_ERR_NOT_INSTALLED; return ipssh_connect_to_port(port); } /* *=========================================================================== * ipssh_stop_all_spawns *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipssh_stop_all_spawns(void) { Ipssh_conn_ctx *ctx; IPSSH_CODE_LOCK(); for (ctx = (Ipssh_conn_ctx*) ipcom_list_first(&(g_ipssh.clt_list)); ctx; ctx = (Ipssh_conn_ctx*) ipcom_list_next(&(ctx->list))) { (void) ipssh_connect_to_port(ctx->stop_port); } IPSSH_CODE_UNLOCK(); return IPCOM_SUCCESS; } #endif /* *=========================================================================== * ipssh_create *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipssh_create(void) { ipcom_list_init(&(g_ipssh.clt_list)); ipcom_list_init(&(g_ipssh.subsystem_init_cb_list)); g_ipssh.task_name_id = 0; if (ipcom_mutex_create(&(g_ipssh.code_mutex)) != IPCOM_SUCCESS) return IPCOM_ERR_FAILED; ipssh_register_subsystem("sftp", ipssh_sftp_subsystem_init_cb); #ifdef IPTESTENGINE ipssh_register_subsystem("ipssh_test", ipssh_test_subsystem_init_cb); #endif return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_store_map_entry *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ipssh_map_entry* ipssh_store_map_entry(Ipssh_conn_ctx *ctx, Ip_fd sock, Ipssh_sock_handler handler, int flags) { Ipssh_map_entry *map_entry; IPCOM_LOG1(DEBUG2, "ipssh_store_map_entry :: sock=%d", sock); map_entry = ipcom_malloc(sizeof(Ipssh_map_entry)); if (!map_entry) { IPCOM_LOG0(ERR, "ipssh_store_map_entry :: out of memory"); return IP_NULL; } map_entry->sock = sock; map_entry->ctx = ctx; map_entry->handler = handler; map_entry->flags = flags; ipcom_list_insert_first(&ctx->ctx_map, (Ipcom_list*) map_entry); return map_entry; } /* *=========================================================================== * ipssh_map_get_max_sock *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_fd ipssh_map_get_max_sock(Ipssh_ctx_map* map) { Ipssh_map_entry *map_entry; Ip_fd max_sock = -1; ip_assert(map->size > 0); for(map_entry = IPCOM_LIST_FIRST(map); map_entry; map_entry = IPCOM_LIST_NEXT(&(map_entry->list))) { if(max_socksock) max_sock = map_entry->sock; } return max_sock; } /* *=========================================================================== * ipssh *=========================================================================== * Description: * Parameters: * Returns: * */ IPCOM_PROCESS(ipssh) { Ip_fd lst_sock[IPSSH_MAX_LISTEN_PORTS]; #ifdef IPSSH_V6_MAPPED_V4_ADDR_NOT_SUPPORTED Ip_fd lst_sock2[IPSSH_MAX_LISTEN_PORTS]; #endif Ip_fd clt_sock; Ip_fd_set* read_set = IP_NULL, *tmp_read_set = IP_NULL; Ip_bool restart = IP_FALSE; int i; Ip_fd max_sock; Ip_fd fd_ipd = IP_INVALID_SOCKET; Ipssh_srv_ctx* srv_ctx = IP_NULL; IPCOM_LOG0(DEBUG2, "ipssh :: starting"); ipcom_proc_init(); IPSSH_CODE_LOCK(); for (i=0; i < IPSSH_MAX_LISTEN_PORTS; i++) { lst_sock[i] = IP_INVALID_SOCKET; #ifdef IPSSH_V6_MAPPED_V4_ADDR_NOT_SUPPORTED lst_sock2[i] = IP_INVALID_SOCKET; #endif } restart: /* Setup server ctx */ IPCOM_LOG0(DEBUG2, "ipssh :: setting up server context"); max_sock = -1; srv_ctx = ipssh_srv_ctx_new(); if (!srv_ctx) { IPCOM_LOG0(ERR, "ipssh :: ipssh_srv_ctx_new() failed"); goto cleanup; } if (ipssh_get_srv_config(srv_ctx) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh :: ipssh_get_srv_config() failed"); goto cleanup; } if (!(read_set = ipcom_malloc(sizeof(Ip_fd_set))) || !(tmp_read_set = ipcom_malloc(sizeof(Ip_fd_set)))) { IPCOM_LOG0(CRIT, "ipssh :: out of memory"); goto cleanup; } ipcom_memset(read_set, 0, sizeof(Ip_fd_set)); ip_assert(srv_ctx->port[0] != 0); /* Set up listen socket */ IPCOM_LOG0(DEBUG2, "ipssh :: setting up listen socket"); for (i=0; iport[i] == 0) break; #ifdef IPCOM_USE_INET6 if (ipssh_setup_listen_sock("::", srv_ctx->port[i], &lst_sock[i]) != IPCOM_SUCCESS) { IPCOM_LOG1(ERR, "ipssh :: ipssh_setup_listen_sock() failed (IPv6), errno=%d", ipcom_errno); goto cleanup; } #ifdef IPSSH_V6_MAPPED_V4_ADDR_NOT_SUPPORTED if (ipssh_setup_listen_sock("0.0.0.0", srv_ctx->port[i], &lst_sock2[i]) != IPCOM_SUCCESS) { IPCOM_LOG1(ERR, "ipssh :: ipssh_setup_listen_sock() failed (IPv4), errno=%d", ipcom_errno); goto cleanup; } IP_FD_SET(lst_sock2[i], read_set); max_sock = IP_MAX(max_sock, lst_sock2[i]); #endif #else if (ipssh_setup_listen_sock("0.0.0.0", srv_ctx->port[i], &lst_sock[i]) != IPCOM_SUCCESS) { IPCOM_LOG1(ERR, "ipssh :: ipssh_setup_listen_sock() failed, errno=%d", ipcom_errno); goto cleanup; } #endif IP_FD_SET(lst_sock[i], read_set); max_sock = IP_MAX(max_sock, lst_sock[i]); } if (fd_ipd == IP_INVALID_SOCKET) /* Only at start, not at reconfigure */ (void)ipcom_ipd_init("ipssh", IPCOM_SUCCESS, &fd_ipd); IP_FD_SET(fd_ipd, read_set); max_sock = IP_MAX(max_sock, fd_ipd); while (1) { IPCOM_LOG0(DEBUG2, "ipssh :: waiting for connection"); ipcom_memcpy(tmp_read_set, read_set, sizeof(Ip_fd_set)); IPSSH_CODE_UNLOCK(); if (ipcom_socketselect(max_sock+1, tmp_read_set, IP_NULL, IP_NULL, IP_NULL) == -1) { IPCOM_LOG1(CRIT, "ipssh :: ipcom_socketselect() failed, errno=%d", ipcom_errno); IPSSH_CODE_LOCK(); goto cleanup; } IPSSH_CODE_LOCK(); for (i=0; i= srv_ctx->max_clients) { IPCOM_LOG0(INFO, "ipssh :: maximum no of connected clients reached, disconnecting client"); ipcom_socketclose(clt_sock); continue; } else { /* * Disable Nagle (enable no delay) on this socket because * we need small packet(= srv_ctx->max_clients) { IPCOM_LOG0(INFO, "ipssh :: maximum no of connected clients reached, disconnecting client"); ipcom_socketclose(clt_sock); continue; } else { ipssh_launch_server(clt_sock, srv_ctx); } } #endif } if (IP_FD_ISSET(fd_ipd, tmp_read_set)) { int event; event = ipcom_ipd_input(fd_ipd); switch(event) { case IPCOM_IPD_EVENT_RECONFIGURE: IPCOM_LOG0(INFO, "ipssh :: received IPD reconfigure message"); restart = IP_TRUE; goto cleanup; case IPCOM_IPD_EVENT_KILL: IPCOM_LOG0(INFO, "ipssh :: received IPD kill message"); goto cleanup; case IPCOM_IPD_EVENT_NONE: break; default: IP_PANIC(); break; } } } cleanup: for (i=0; isock = sock; spawn_args->srv_ctx = ipssh_srv_ctx_dup(srv_ctx); if (ipcom_ipc_send(&ipc, spawn_args)) { ipcom_socketclose(sock); ipcom_ipc_free(spawn_args); IPCOM_LOG0(ERR, "ipssh_launch_server :: ipcom_ipc_send() failed"); } #ifdef IP_PORT_OSE5 { /* OSE5 has process specific sockets -> donate child fd */ Ip_err retval; retval = (Ip_err)efs_donate_fd(sock, pid); if (retval != 0) { ipcom_socketclose(sock); IPCOM_LOG0(ERR, "ipssh_launch_server :: efs_donate_fd() failed"); } } #endif /* IP_PORT_OSE5 */ (void)ipcom_ipc_close(&ipc); } /* *=========================================================================== * ipssh_server *=========================================================================== * Description: * Parameters: * Returns: * */ IPCOM_PROCESS(ipssh_server) { Ipssh_conn_ctx *ctx = IP_NULL; Ipssh_map_entry *map_entry; Ip_s32 on = 1; Ip_fd sock; struct Ip_timeval idle_timeout; Ipssh_spawn_args *spawn_args; void* sync; ipcom_proc_init(); ctx = ipssh_conn_ctx_new(); if (!ctx) { IPCOM_LOG0(ERR, "ipssh_server :: ipssh_conn_ctx_new() failed"); ipcom_proc_exit(); return; } if (ipcom_ipc_install() != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_server :: ipcom_ipc_install() failed"); goto exit; } if (ipcom_ipc_receive(IP_NULL, &sync, IPC_TIMEOUT_INFINITE) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_server :: ipcom_ipc_receive() failed"); goto exit; } ipssh_register_clt(ctx); if (ipcom_ipc_send(IPCOM_IPC_SENDER, sync) != IPCOM_SUCCESS) { ipcom_ipc_free(sync); IPCOM_LOG0(ERR, "ipssh_server :: ipcom_ipc_send() failed"); goto exit; } if (ipcom_ipc_receive(IP_NULL, &spawn_args, IPC_TIMEOUT_INFINITE) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_server :: ipcom_ipc_receive() failed"); goto exit; } (void)ipcom_ipc_uninstall(); IPSSH_CODE_LOCK(); /* Change the owner of the telnet socket to this task */ #ifdef IP_PORT_OSE5 spawn_args->sock = efs_receive_fd(0); if (spawn_args->sock == -1) { IPCOM_LOG0(ERR, "ipssh_server :: efs_receive_fd() failed"); goto exit; } #endif #if defined(IP_PORT_OSE) || defined(IP_PORT_OSE5) { Ip_pid_t pid; pid = ipcom_getpid(); if (ipcom_socketioctl(spawn_args->sock, IP_SIOCSPGRP, &pid) < 0) { IPCOM_LOG1(ERR, "ipssh_server :: ipcom_socketioctl(IP_SIOCSPGRP) failed, errno = %d", ipcom_errno); goto exit; } } #endif /* IP_PORT_OSE */ sock = spawn_args->sock; ipssh_conn_ctx_set_srv_ctx(ctx, spawn_args->srv_ctx); ipssh_srv_ctx_free(spawn_args->srv_ctx); ctx->ssh_conn->mode = IPSSH_MODE_SRV; ctx->pid = ipcom_getpid(); ctx->ssh_conn->sock = spawn_args->sock; ipcom_ipc_free(spawn_args); IPSSH_CODE_UNLOCK(); idle_timeout.tv_sec = ctx->srv_ctx->idle_timeout; ip_assert(idle_timeout.tv_sec != -1); idle_timeout.tv_usec = 0; map_entry = ipssh_store_map_entry(ctx, sock, ipssh_read_version_str, IPSSH_FLAG_NO_CLOSE); if (!map_entry) { IPCOM_LOG0(ERR, "ipssh_server :: ipssh_store_map_entry() failed"); goto exit; } if (ipcom_socketioctl(sock, IP_FIONBIO, (char *)&on)) { IPCOM_LOG1(ERR, "ipssh_server :: ipcom_socketioctl() failed, errno=%d", ipcom_errno); goto exit; } IP_FD_SET(sock, &(ctx->read_set)); #ifdef IPSSH_USE_STOP_SPAWN { /* Set up a listen socket bound to localhost. When the spawned ssh server shall be stopped * a connection is made to the listen socket. The select call below will then return and the * handler function for this socket will return IPCOM_ERR_FAILED that will make the server * close the connection with the client and terminate */ if (ipssh_setup_stop_sock(&(ctx->stop_sock), &(ctx->stop_port)) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_server :: ipssh_setup_stop_sock() failed"); goto exit; } map_entry = ipssh_store_map_entry(ctx, ctx->stop_sock, ipssh_stop_spawned_server, 0); if (!map_entry) { IPCOM_LOG0(ERR, "ipssh_server :: ipssh_store_map_entry() failed"); goto exit; } IP_FD_SET(ctx->stop_sock, &(ctx->read_set)); } #endif /* Send version-strings */ { char buf[32]; Ip_size_t str_len; if (ipssh_make_version_str(ctx->srv_ctx->config.v1, ctx->srv_ctx->config.v2, buf, sizeof(buf)) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_server :: ipssh_make_version_str() failed"); goto exit; } if (ipcom_send(ctx->ssh_conn->sock, buf, ipcom_strlen(buf), 0) != (int)ipcom_strlen(buf)) { IPCOM_LOG1(ERR, "ipssh_server :: ipcom_send() failed, errno = %d", ipcom_errno); goto exit; } /* Remove trailing \r\n, not to be included in digital signature */ str_len = ipcom_strlen(buf); while(str_len > 0 && (buf[str_len-1] == '\r' || buf[str_len-1] == '\n')) { str_len--; buf[str_len] = 0; } /* Store version string */ if (ipssh_msg2list((Ipssh_msg_list*)&(ctx->ssh_conn->handshake_msgs), IPSSH_STR, (Ip_u8 *)buf, str_len, 1)) { IPCOM_LOG0(ERR, "ipssh_server :: ipssh_msg2list() failed"); goto exit; } } while(1) { Ip_fd_set tmp_read_set, tmp_write_set; Ip_s32 rc; Ipcom_list *entry; again: ipcom_memcpy(&tmp_read_set, &(ctx->read_set), sizeof(Ip_fd_set)); ipcom_memcpy(&tmp_write_set, &(ctx->write_set), sizeof(Ip_fd_set)); rc = ipcom_socketselect(ipssh_map_get_max_sock(&(ctx->ctx_map))+1, &tmp_read_set, &tmp_write_set, IP_NULL, idle_timeout.tv_sec != 0 ? &idle_timeout : IP_NULL); switch(rc) { case -1: IPCOM_LOG1(DEBUG, "ipcom_socketselect() failed, errno=%d", ipcom_errno); goto exit; case 0: IPCOM_LOG0(INFO, "disconnecting client due to idle timeout"); goto exit; default: IPCOM_LOG1(DEBUG2, "ipssh_server: ipcom_socketselect() return %d", rc); break; } for (entry = ipcom_list_first(&ctx->ctx_map); entry != IP_NULL;) { map_entry = (Ipssh_map_entry*) entry; /* Advance entry prior to handler invocation, since it may delete it */ entry = ipcom_list_next(entry); if (IP_FD_ISSET(map_entry->sock, &tmp_read_set) || IP_FD_ISSET(map_entry->sock, &tmp_write_set)) { if (map_entry->handler(map_entry->sock, ctx, map_entry)) { /* close connection */ goto exit; } rc--; if (rc==0) goto again; } } } exit: ipssh_unregister_clt(ctx); #ifdef IP_PORT_VXWORKS if ( (_func_logoutEvent != IP_NULL) && (ctx->ssh_conn->conn_state == IPSSH_CONN_STATE_SRV_TERM) ) _func_logoutEvent(SEC_EVENT_SSH_LOGOUT, ctx->ssh_conn->user); #endif if (ctx) { ipssh_close_conn(ctx); } ERR_remove_state(0); /* * Increase the IPSSH process priority to prevent from IPSSH daemon task pending in creating * another IPSSH process with same name while it has not been deleted completely. */ (void)ipcom_proc_setprio(ipcom_getpid(), IPSSH_EXIT_PROC_PRIO); ipcom_proc_exit(); } /* *=========================================================================== * ipssh_close_conn *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC void ipssh_close_conn(Ipssh_conn_ctx *ctx) { ipssh_conn_free(ctx->ssh_conn); ipssh_conn_ctx_free(ctx); } /* *=========================================================================== * ipssh_make_version_str *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_make_version_str(int v1, int v2, char *buf, int buf_len) { int vers_major; int vers_minor; int fips_mode; ip_assert(v1 || v2); #ifdef OPENSSL_FIPS fips_mode = FIPS_mode(); #else fips_mode = 0; #endif #ifdef IPSSH_USE_V1 if (v1 && !fips_mode && v2) { vers_major = 1; vers_minor = 99; } else if (v2) { vers_major = 2; vers_minor = 0; } else if (v1 && !fips_mode) { vers_major = 1; vers_minor = 5; } else { return IPCOM_ERR_FAILED; } #else vers_major = 2; vers_minor = 0; #endif ipcom_snprintf(buf, buf_len, "SSH-%d.%d-IPSSH-" IPSSH_VERSION "\r\n", vers_major, vers_minor); return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_read_version_str *=========================================================================== * Description: Read client's version string * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_read_version_str(Ip_fd sock, Ipssh_conn_ctx *ctx, Ipssh_map_entry *map_entry) { Ip_size_t str_len; union Ip_sockaddr_union sa; Ip_socklen_t addr_len; IPCOM_LOG1(DEBUG, "ipssh_read_version_str(), sock=%d", sock); str_len = IPSSH_VERSION_STRING_MAX_LEN - 1; if (ipssh_read_from_socket(ctx->ssh_conn->sock, ctx->version_str, &str_len, "\n") != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_read_version_str :: ipssh_read_from_socket() failed"); return IPCOM_ERR_FAILED; } if (str_len < 9) /* A valid version string cannot be shorter than 9 bytes */ { IPCOM_LOG1(ERR, "ipssh_read_version_str :: too short version string, %d bytes", str_len); return IPCOM_ERR_FAILED; } /* Strip off any CRLF */ ctx->version_str[str_len-1] = 0; if (ctx->version_str[str_len-2] == '\r') /* strip off any CR */ ctx->version_str[str_len-2] = 0; if (ipssh_msg2list((Ipssh_msg_list*)&(ctx->ssh_conn->handshake_msgs), IPSSH_STR, (Ip_u8 *)ctx->version_str, (Ip_u32)ipcom_strlen(ctx->version_str), (Ip_u8)0)) { IPCOM_LOG0(ERR, "ipssh_read_version_str :: ipssh_msg2list() failed"); return IPCOM_ERR_FAILED; } /* Add local addr of TCP connection to shell params */ addr_len = sizeof(sa); if (ipcom_getsockname(sock, (struct Ip_sockaddr *)&sa, &addr_len) == IP_SOCKERR) { IPCOM_LOG1(ERR, "ipssh_read_version_str :: ipcom_getsockname(client_fd) failed, errno = %d", ipcom_errno); return IPCOM_ERR_FAILED; } if (ipcom_shell_ctx_add_local_addr(ctx->ssh_conn->sh_ctx, &sa) != IPCOM_SUCCESS) return IPCOM_ERR_FAILED; /* Add peer addr of TCP connection to shell params */ addr_len = sizeof(sa); if (ipcom_getpeername(sock, (struct Ip_sockaddr *)&sa, &addr_len) == IP_SOCKERR) { IPCOM_LOG1(ERR, "ipssh_read_version_str :: ipcom_getpeername(client_fd) failed, errno = %d", ipcom_errno); return IPCOM_ERR_FAILED; } if (ipcom_shell_ctx_add_peer_addr(ctx->ssh_conn->sh_ctx, &sa) != IPCOM_SUCCESS) return IPCOM_ERR_FAILED; if (ipcom_shell_ctx_add_int(ctx->ssh_conn->sh_ctx, IPCOM_SHELL_CTX_INVOKER, IPCOM_SHELL_CTX_INVOKER_SSH) != IPCOM_SUCCESS) return IPCOM_ERR_FAILED; if (ipcom_shell_ctx_add_int(ctx->ssh_conn->sh_ctx, IPSSH_CLT_FD, sock) != IPCOM_SUCCESS) return IPCOM_ERR_FAILED; /* Parse version string */ { Ip_s32 count, proto_ver_major, proto_ver_minor; count = ipcom_sscanf(ctx->version_str, "SSH-%d.%d-%s ", (int*)&proto_ver_major, (int*)&proto_ver_minor, ctx->clt_id); if (count != 3) { IPCOM_LOG0(ERR, "ipssh_read_version_str :: failed to parse version str"); return IPCOM_ERR_FAILED; } /* Add client specific flags here. */ if (ipcom_strncmp(ctx->clt_id, "PuTTY", ipcom_strlen("PuTTY")) == 0) { ctx->ssh_conn->flags = IPSSH_FLAG_BUG_UNRESTRICTED_SFTP_MSG_SIZE; } switch(ipssh_get_conn_version(proto_ver_major, proto_ver_minor, ctx->srv_ctx->config.v1, ctx->srv_ctx->config.v2, ctx->srv_ctx->config.v1 && ctx->srv_ctx->config.v2)) { #ifdef IPSSH_USE_V1 case 1: #ifdef OPENSSL_FIPS if (FIPS_mode()) { IPCOM_LOG0(INFO, "ipssh_read_version_str :: SSH v1 support not allowed in FIPS mode"); return IPCOM_ERR_FAILED; } #endif ctx->ssh_conn->ssh_version = 1; return ipssh_initiate_v1_connection(ctx->ssh_conn, map_entry); #else case 1: IPCOM_LOG0(INFO, "ipssh_read_version_str :: SSH v1 support not compiled in"); return IPCOM_ERR_FAILED; #endif case 2: ctx->ssh_conn->ssh_version = 2; return ipssh_initiate_v2_connection(ctx->ssh_conn, map_entry); default: IPCOM_LOG2(ERR, "ipssh_read_version_str :: unsupported version, %d.%d", proto_ver_major, proto_ver_minor); return IPCOM_ERR_FAILED; } } } /* *=========================================================================== * ipssh_get_conn_version *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL int ipssh_get_conn_version(int major_ver, int minor_ver, int v1, int v2, Ip_bool comp_mode) { #ifdef OPENSSL_FIPS int fips_mode = FIPS_mode(); #endif switch(major_ver) { case 1: /* This is a request to run v2, if possible */ if (minor_ver == 99) { if (!comp_mode) { IPCOM_LOG0(INFO, "ipssh_get_conn_version :: SSH compatibility mode not turned on"); return 0; } if (v2) { return 2; } } #ifdef IPSSH_USE_V1 /* Check that SSH v1 is active */ if (!v1) { IPCOM_LOG0(INFO, "ipssh_get_conn_version :: SSH v1 support not turned on"); return 0; } #ifdef OPENSSL_FIPS if (fips_mode) { IPCOM_LOG0(INFO, "ipssh_get_conn_version :: SSH v1 support not allowed in FIPS mode"); return 0; } #endif return 1; #else IPCOM_LOG0(INFO, "ipssh_get_conn_version :: SSH v1 support not compiled in"); return 0; #endif case 2: #ifdef IPSSH_USE_V1 /* Check that SSH v2 is turned on */ if (!v2) { IPCOM_LOG0(ERR, "ipssh_get_conn_version :: SSH v2 support not turned on"); return 0; } #endif return 2; default: IPCOM_LOG2(ERR, "ipssh_get_conn_version :: unsupported version, %d.%d", major_ver, minor_ver); return 0; } } /* *=========================================================================== * ipssh_conn_ctx_new *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC void ipssh_conn_ctx_set_srv_ctx(Ipssh_conn_ctx * ctx, Ipssh_srv_ctx *srv_ctx) { ipcom_memcpy(&ctx->ssh_conn->config, &srv_ctx->config, sizeof(struct Ipssh_conn_config_st)); ctx->srv_ctx = ipssh_srv_ctx_dup(srv_ctx); if (srv_ctx->dsa_key) { ctx->ssh_conn->srv_dsa_key = srv_ctx->dsa_key; (void)DSA_up_ref(srv_ctx->dsa_key); } #ifdef IPSSH_USE_RSA if (srv_ctx->rsa_key) { ctx->ssh_conn->srv_rsa_key = srv_ctx->rsa_key; (void)RSA_up_ref(srv_ctx->rsa_key); } if (srv_ctx->rsa_keyex_key) { ctx->ssh_conn->srv_rsa_keyex_key = srv_ctx->rsa_keyex_key; (void)RSA_up_ref(srv_ctx->rsa_keyex_key); } #endif } /* *=========================================================================== * ipssh_conn_ctx_new *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ipssh_conn_ctx * ipssh_conn_ctx_new(void) { Ipssh_conn_ctx *ctx; ctx = ipcom_malloc(sizeof(Ipssh_conn_ctx)); if (!ctx) { IPCOM_LOG1(ERR, "ipssh_conn_ctx_new :: ipcom_malloc() failed, errno=%d", ipcom_errno); return IP_NULL; } ipcom_memset(ctx, 0, sizeof(Ipssh_conn_ctx)); ctx->version_str = ipcom_malloc(IPSSH_VERSION_STRING_MAX_LEN+1); ctx->clt_id = ipcom_malloc(IPSSH_VERSION_STRING_MAX_LEN+1); ctx->ssh_conn = ipssh_conn_new(); if (!ctx->version_str || !ctx->clt_id || !ctx->ssh_conn) { IPCOM_LOG0(ERR, "ipssh_conn_ctx_new :: ipcom_malloc() failed"); goto cleanup; } ipcom_list_init(&ctx->ctx_map); ctx->ssh_conn->ssh_conn_ctx = ctx; ipcom_memset(ctx->version_str, 0, IPSSH_VERSION_STRING_MAX_LEN+1); ctx->ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_KEX_INIT; ctx->ssh_conn->port_fwd_cb = ipssh_port_fwd_cb; ctx->ssh_conn->extra = ctx; return ctx; cleanup: ipssh_conn_ctx_free(ctx); return IP_NULL; } /* *=========================================================================== * ipssh_conn_ctx_free *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC void ipssh_conn_ctx_free(Ipssh_conn_ctx *ctx) { Ipssh_map_entry *map_entry; ip_assert(ctx); if (ctx->version_str) ipcom_free(ctx->version_str); if (ctx->clt_id) ipcom_free(ctx->clt_id); for (map_entry = (Ipssh_map_entry*)ipcom_list_first(&ctx->ctx_map); map_entry; map_entry = (Ipssh_map_entry*)ipcom_list_first(&ctx->ctx_map)) { if (map_entry->sock != IP_INVALID_SOCKET && !(map_entry->flags & IPSSH_FLAG_NO_CLOSE)) ipcom_socketclose(map_entry->sock); ipssh_delete_map_entry_for_sock(&ctx->ctx_map, map_entry->sock); } if (ctx->srv_ctx) ipssh_srv_ctx_free(ctx->srv_ctx); ipcom_free(ctx); } /* *=========================================================================== * ipssh_get_len *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_u32 ipssh_get_len(Ip_u8 *ptr) { Ip_u32 tmp; tmp = ptr[0] << 24; tmp += ptr[1] << 16; tmp += ptr[2] << 8; tmp += ptr[3]; return tmp; } /* *=========================================================================== * ipssh_read_from_socket *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_read_from_socket(Ip_fd sock, char *buf, Ip_size_t* buf_len, const char *delim) { Ip_size_t pos; Ip_ssize_t rc; int tries = 0; IPCOM_LOG1(DEBUG2, "ipssh_read_from_socket(), sock=%d", sock); for (pos=0; pos<*buf_len; pos++) { retry: rc = ipcom_socketread(sock, &(buf[pos]), 1); if (rc < 0) { if (ipcom_errno == IP_ERRNO_EWOULDBLOCK) { ipcom_millisleep(100); tries++; if (tries < 100) goto retry; } IPCOM_LOG1(ERR, "ipssh_read_from_socket :: ipcom_socketread() failed, errno=%d", ipcom_errno); return IPCOM_ERR_FAILED; } if (rc == 0) { IPCOM_LOG0(ERR, "ipssh_read_from_socket :: peer closed connection"); return IPCOM_ERR_FAILED; } if (ipcom_strstr(buf, delim) != IP_NULL) { *buf_len = pos + 1; buf[pos+1] = 0; return IPCOM_SUCCESS; } } return IPCOM_ERR_FAILED; } /* *=========================================================================== * ipssh_get_clt_info *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipssh_get_clt_info(Ipssh_clt_info* clt_info, int pos) { Ipssh_conn_ctx *ctx; union Ip_sockaddr_union addr; Ip_socklen_t addr_len; void *addr_ptr; int i; ip_assert(clt_info); ipcom_memset(clt_info, 0, sizeof(Ipssh_clt_info)); IPSSH_CODE_LOCK(); if (g_ipssh.clt_list.size == 0 || (Ip_u32)pos >= g_ipssh.clt_list.size) { IPSSH_CODE_UNLOCK(); return IPCOM_ERR_NOT_FOUND; } /* Iterate to the requested position */ for (i=0, ctx = IPCOM_LIST_FIRST(&(g_ipssh.clt_list)); ilist)), i++); /* This check is unnecessary. Just to make Coverity happy */ if (!ctx) { IPSSH_CODE_UNLOCK(); return IPCOM_ERR_NOT_FOUND; } switch(ctx->ssh_conn->conn_state) { case IPSSH_CONN_STATE_SRV_KEX_INIT: case IPSSH_CONN_STATE_SRV_KEX_DH_INIT: case IPSSH_CONN_STATE_SRV_NEW_KEY: case IPSSH_CONN_STATE_SERVICE_REQ: case IPSSH_CONN_STATE_SRV_KEY_EX_V1: case IPSSH_CONN_STATE_SRV_PENDING_REQUEST: case IPSSH_CONN_STATE_SRV_USER_AUTH: case IPSSH_CONN_STATE_SRV_KEX_GEX: case IPSSH_CONN_STATE_SRV_KEX_RSA: ipcom_strncpy(clt_info->service, "in handshake", sizeof(clt_info->service)); break; case IPSSH_CONN_STATE_SRV_TERM: ipcom_strncpy(clt_info->service, "terminal", sizeof(clt_info->service)); break; case IPSSH_CONN_STATE_SRV_SSH_SFTP: ipcom_strncpy(clt_info->service, "sftp", sizeof(clt_info->service)); break; default: IP_PANIC(); break; } addr_len = sizeof(addr); (void)ipcom_getpeername(ctx->ssh_conn->sock, &addr.sa, &addr_len); #ifdef IPCOM_USE_INET6 if(addr.sa.sa_family == IP_AF_INET6) addr_ptr = &addr.sin6.sin6_addr; else #endif addr_ptr = &addr.sin.sin_addr; (void)ipcom_inet_ntop(addr.sa.sa_family, addr_ptr, clt_info->clt_addr, sizeof(clt_info->clt_addr)); clt_info->pid = ctx->pid; if (ctx->ssh_conn->conn_state == IPSSH_CONN_STATE_SRV_TERM || ctx->ssh_conn->conn_state == IPSSH_CONN_STATE_SRV_SSH_SFTP) { ipcom_strncpy(clt_info->userid, ctx->ssh_conn->user, sizeof(clt_info->userid)); switch(ctx->ssh_conn->auth_meth) { case IPSSH_AUTH_METH_NONE: ipcom_strncpy(clt_info->auth_method, "none", sizeof(clt_info->auth_method)); break; case IPSSH_AUTH_METH_PUBLIC_KEY: ipcom_strncpy(clt_info->auth_method, "public key", sizeof(clt_info->auth_method)); break; case IPSSH_AUTH_METH_PW: ipcom_strncpy(clt_info->auth_method, "password", sizeof(clt_info->auth_method)); break; case IPSSH_AUTH_METH_HOSTBASED: ipcom_strncpy(clt_info->auth_method, "hostbased", sizeof(clt_info->auth_method)); break; default: IP_PANIC(); } if (ctx->ssh_conn->ssh_version == 2) { ipcom_strncpy(clt_info->cipher, ipssh_alg_id2str(ctx->ssh_conn->conn_prop.enc_alg_clt2srv[0]), sizeof(clt_info->cipher)); ipcom_strncpy(clt_info->mac, ipssh_alg_id2str(ctx->ssh_conn->conn_prop.mac_alg_clt2srv[0]), sizeof(clt_info->mac)); } #ifdef IPSSH_USE_V1 else { switch(ctx->ssh_conn->cipher) { case IPSSH_V1_CIPHER_3DES: ipcom_strncpy(clt_info->cipher, "3des", sizeof(clt_info->cipher)); break; case IPSSH_V1_CIPHER_DES: ipcom_strncpy(clt_info->cipher, "des", sizeof(clt_info->cipher)); break; case IPSSH_V1_CIPHER_RC4: ipcom_strncpy(clt_info->cipher, "RC4", sizeof(clt_info->cipher)); break; case IPSSH_V1_CIPHER_BLOWFISH: ipcom_strncpy(clt_info->cipher, "blowfish", sizeof(clt_info->cipher)); break; case IPSSH_V1_CIPHER_NONE: ipcom_strncpy(clt_info->cipher, "none", sizeof(clt_info->cipher)); break; default: IP_PANIC(); } ipcom_strncpy(clt_info->mac, "crc32", sizeof(clt_info->mac)); } #endif ipcom_strncpy(clt_info->ssh_clt_sw, ctx->clt_id, sizeof(clt_info->ssh_clt_sw)-1); } IPSSH_CODE_UNLOCK(); return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_conn_init *=========================================================================== */ IP_GLOBAL Ip_err ipssh_conn_init(Ipssh_conn *ssh_conn) { ipcom_memset(ssh_conn, 0, sizeof(Ipssh_conn)); (void)EVP_EncryptInit(&(ssh_conn->send_ctx), EVP_enc_null(), IP_NULL, IP_NULL); (void)EVP_DecryptInit(&(ssh_conn->recv_ctx), EVP_enc_null(), IP_NULL, IP_NULL); ssh_conn->sock = IP_INVALID_SOCKET; ssh_conn->dh_data = ipcom_malloc(sizeof(Ipssh_kex_dh_data)); if (!ssh_conn->dh_data) { IPCOM_LOG0(WARNING, "ipssh_conn_init :: out of memory"); return IPCOM_ERR_FAILED; } ssh_conn->sh_ctx = ipcom_shell_ctx_new(128); if (!ssh_conn->sh_ctx) { ipcom_free(ssh_conn->dh_data); IPCOM_LOG0(WARNING, "ipssh_conn_init :: out of memory"); return IPCOM_ERR_FAILED; } ipcom_memset(ssh_conn->dh_data, 0, sizeof(Ipssh_kex_dh_data)); ipcom_list_init((Ipcom_list*)&(ssh_conn->handshake_msgs)); ipcom_list_init(&ssh_conn->ch_map); ipcom_list_init(&ssh_conn->port_fwd_addr_map); return ipssh_get_config(&ssh_conn->config); } /* *=========================================================================== * ipssh_conn_new *=========================================================================== */ IP_GLOBAL Ipssh_conn* ipssh_conn_new(void) { Ipssh_conn *ssh_conn; ssh_conn = ipcom_malloc(sizeof(Ipssh_conn)); if (!ssh_conn) return IP_NULL; if (ipssh_conn_init(ssh_conn) != IPCOM_SUCCESS) { ipcom_free(ssh_conn); return IP_NULL; } return ssh_conn; } /* *=========================================================================== * ipssh_free_handshake_msgs *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL void ipssh_free_handshake_msgs(Ipssh_conn *ssh_conn, int flags) { Ipcom_list *entry; for (entry = ipcom_list_first((Ipcom_list*)&(ssh_conn->handshake_msgs)); entry; ) { Ipssh_msg_list* msg_entry = (Ipssh_msg_list*) entry; if (flags & IPSSH_HANDSHAKE_MSG_FLAG_SAVE_VER_STR) { /* The clt and srv version string have pos 1 and 2, respectively */ if (msg_entry->hash_pos == 0 || msg_entry->hash_pos == 1) { entry = ipcom_list_next(&msg_entry->list); continue; } } entry = ipcom_list_next(&msg_entry->list); ipcom_list_remove((Ipcom_list*)msg_entry); if (msg_entry->value) ipcom_free(msg_entry->value); ipcom_free(msg_entry); } } /* *=========================================================================== * ipssh_conn_free *=========================================================================== */ IP_GLOBAL void ipssh_conn_free(Ipssh_conn *ssh_conn) { Ipcom_list *entry; for (entry = ipcom_list_first((Ipcom_list*)&ssh_conn->ch_map); entry; entry = ipcom_list_first((Ipcom_list*)&ssh_conn->ch_map)) { Ipssh_channel* ch = (Ipssh_channel*) entry; ipssh_channel_close(ch); } if (ssh_conn->dh_data) { if (ssh_conn->dh_data->e) BN_free(ssh_conn->dh_data->e); if (ssh_conn->dh_data->f) BN_free(ssh_conn->dh_data->f); if (ssh_conn->dh_data->dh) DH_free(ssh_conn->dh_data->dh); ipcom_free(ssh_conn->dh_data); } ipssh_free_handshake_msgs(ssh_conn, 0); HMAC_cleanup(&(ssh_conn->recv_hmac_ctx)); HMAC_cleanup(&(ssh_conn->send_hmac_ctx)); (void)EVP_CIPHER_CTX_cleanup(&(ssh_conn->send_ctx)); (void)EVP_CIPHER_CTX_cleanup(&(ssh_conn->recv_ctx)); if (ssh_conn->shared_secret) ipcom_free(ssh_conn->shared_secret); if (ssh_conn->hash) ipcom_free(ssh_conn->hash); if (ssh_conn->key_mtrl.iv_c2s.key) ipcom_free(ssh_conn->key_mtrl.iv_c2s.key); if (ssh_conn->key_mtrl.iv_s2c.key) ipcom_free(ssh_conn->key_mtrl.iv_s2c.key); if (ssh_conn->key_mtrl.key_c2s.key) ipcom_free(ssh_conn->key_mtrl.key_c2s.key); if (ssh_conn->key_mtrl.key_s2c.key) ipcom_free(ssh_conn->key_mtrl.key_s2c.key); if (ssh_conn->key_mtrl.mac_key_c2s.key) ipcom_free(ssh_conn->key_mtrl.mac_key_c2s.key); if (ssh_conn->key_mtrl.mac_key_c2s_active.key) ipcom_free(ssh_conn->key_mtrl.mac_key_c2s_active.key); if (ssh_conn->key_mtrl.mac_key_s2c.key) ipcom_free(ssh_conn->key_mtrl.mac_key_s2c.key); if (ssh_conn->key_mtrl.mac_key_s2c_active.key) ipcom_free(ssh_conn->key_mtrl.mac_key_s2c_active.key); /* v1 data */ /* 3DES in ssh v1 uses a 24 byte iv, which doesn't fit into the iv field of EVP_CIPHER_CTX. * Instead it is stored in the app_data field */ if (ssh_conn->send_ctx.app_data) ipcom_free(ssh_conn->send_ctx.app_data); if (ssh_conn->recv_ctx.app_data) ipcom_free(ssh_conn->recv_ctx.app_data); if (ssh_conn->sess_id) ipcom_free(ssh_conn->sess_id); if (ssh_conn->user) ipcom_free(ssh_conn->user); if (ssh_conn->service) ipcom_free(ssh_conn->service); if (ssh_conn->recv_enc_buf) ipcom_buffer_free(ssh_conn->recv_enc_buf); if (ssh_conn->recv_plain_buf) ipcom_buffer_free(ssh_conn->recv_plain_buf); for (entry = ipcom_list_first(&ssh_conn->port_fwd_addr_map); entry; entry = ipcom_list_first(&ssh_conn->port_fwd_addr_map)) { Ipssh_port_fwd_addr_map* addr_map = (Ipssh_port_fwd_addr_map*) entry; ipcom_list_remove(entry); ipcom_free(addr_map->addr); ipcom_free(addr_map); } if (ssh_conn->srv_dsa_key) DSA_free(ssh_conn->srv_dsa_key); #ifdef IPSSH_USE_RSA if (ssh_conn->srv_rsa_key) RSA_free(ssh_conn->srv_rsa_key); if (ssh_conn->srv_rsa_keyex_key) RSA_free(ssh_conn->srv_rsa_keyex_key); #endif #ifdef IPSSH_USE_V1 if (ssh_conn->server_key) RSA_free(ssh_conn->server_key); if (ssh_conn->client_pub_key) RSA_free(ssh_conn->client_pub_key); #endif if (ssh_conn->sock != IP_INVALID_SOCKET) ipcom_socketclose(ssh_conn->sock); if (ssh_conn->sh_ctx) ipcom_shell_ctx_free(ssh_conn->sh_ctx); /* SSH clt data */ if (ssh_conn->data.clt.key) EVP_PKEY_free(ssh_conn->data.clt.key); if (ssh_conn->data.clt.host_key) EVP_PKEY_free(ssh_conn->data.clt.host_key); if (ssh_conn->data.clt.srv_pub_key) EVP_PKEY_free(ssh_conn->data.clt.srv_pub_key); ipcom_free(ssh_conn); } /* *=========================================================================== * ipssh_version *=========================================================================== */ IP_PUBLIC const char * ipssh_version(void) { return "@(#) IPSSH $Name: VXWORKS_ITER40_RCPL8_2024030303 $ - INTERPEAK_COPYRIGHT_STRING"; } /* *=========================================================================== * ipssh_start *=========================================================================== */ IP_PUBLIC Ip_err ipssh_start(void) { Ip_err ret; Ipcom_proc_attr attr; /* Start daemon */ IPCOM_LOG0(INFO, "ipssh_start :: starting \"ipssh\" process"); ipcom_proc_attr_init(&attr); attr.priority = IPSSH_DAEMON_PROC_PRIO; attr.stacksize = IPCOM_PROC_STACK_MAX; attr.flags = IPCOM_PROC_FLAG_FP; ret = ipcom_proc_acreate(IPSSH_DAEMON_NAME, ipssh, &attr, IP_NULL); ip_assert(ret == IPCOM_SUCCESS); return ret; } /* *=========================================================================== * ipssh_alg_id2str *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL const char * ipssh_alg_id2str(Ip_u8 id) { Ipssh_config_map* map; for (map = ipssh_alg_ids; map->str != IP_NULL; map++) { if (id == map->id) return map->str; } return IP_NULL; } /* *=========================================================================== * ipssh_alg_str2id *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_u8 ipssh_alg_str2id(const char *str) { Ipssh_config_map* map; for (map = ipssh_alg_ids; map->str != IP_NULL; map++) { if (ipcom_strcmp(str, map->str) == 0) return map->id; } /* No match found */ return IPSSH_ID_UNKNOWN; } #ifdef IPSSH_USE_V1 /* *=========================================================================== * ipssh_port_fwd_cb_v1 *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_port_fwd_cb_v1(Ipssh_conn_ctx *ctx, Ip_fd port_fwd_sock, int event) { switch(event) { case IPSSH_PORT_FWD_EVENT_LISTEN: if (!ipssh_store_map_entry(ctx, port_fwd_sock, ipssh_port_fwd_listen_handler_v1, 0)) return IPCOM_ERR_FAILED; IP_FD_SET(port_fwd_sock, &(ctx->read_set)); break; case IPSSH_PORT_FWD_EVENT_OPEN: if (!ipssh_store_map_entry(ctx, port_fwd_sock, ipssh_port_fwd_handler_s2c_v1, 0)) return IPCOM_ERR_FAILED; IP_FD_SET(port_fwd_sock, &(ctx->read_set)); break; case IPSSH_PORT_FWD_EVENT_CLOSE: IP_FD_CLR(port_fwd_sock, &(ctx->read_set)); ipssh_delete_map_entry_for_sock(&ctx->ctx_map, port_fwd_sock); break; default: IP_PANIC(); return IPCOM_ERR_FAILED; } return IPCOM_SUCCESS; } #endif /* *=========================================================================== * ipssh_port_fwd_cb_v2 *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_port_fwd_cb_v2(Ipssh_conn_ctx *ctx, Ip_fd port_fwd_sock, int event) { switch(event) { case IPSSH_PORT_FWD_EVENT_LISTEN: if (!ipssh_store_map_entry(ctx, port_fwd_sock, ipssh_port_fwd_listen_handler, 0)) return IPCOM_ERR_FAILED; IP_FD_SET(port_fwd_sock, &(ctx->read_set)); break; case IPSSH_PORT_FWD_EVENT_OPEN: if (!ipssh_store_map_entry(ctx, port_fwd_sock, ipssh_port_fwd_handler_s2c, 0)) return IPCOM_ERR_FAILED; IP_FD_SET(port_fwd_sock, &(ctx->read_set)); break; case IPSSH_PORT_FWD_EVENT_SEND_WINDOW_OPEN: IP_FD_SET(port_fwd_sock, &(ctx->read_set)); break; case IPSSH_PORT_FWD_EVENT_SEND_WINDOW_CLOSED: IP_FD_CLR(port_fwd_sock, &(ctx->read_set)); break; case IPSSH_PORT_FWD_EVENT_CLOSE: IP_FD_CLR(port_fwd_sock, &(ctx->read_set)); ipssh_delete_map_entry_for_sock(&ctx->ctx_map, port_fwd_sock); break; default: IP_PANIC(); return IPCOM_ERR_FAILED; } return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_port_fwd_cb *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_port_fwd_cb(Ipssh_conn *ssh_conn, Ip_fd port_fwd_sock, int event) { Ipssh_conn_ctx *ctx; ctx = (Ipssh_conn_ctx*)ssh_conn->extra; #ifdef IPSSH_USE_V1 if (ssh_conn->ssh_version == 1) return ipssh_port_fwd_cb_v1(ctx, port_fwd_sock, event); else #endif return ipssh_port_fwd_cb_v2(ctx, port_fwd_sock, event); } /* *=========================================================================== * ipssh_conn_get_fd *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_fd ipssh_conn_get_fd(Ipssh_conn* ssh_conn) { return ssh_conn->sock; } /* **************************************************************************** * END OF FILE **************************************************************************** */