Files
m241Neelima/components/ip_net2-6.9/ipssh/src/ipssh.c
T
2026-07-30 11:19:44 +05:30

2777 lines
82 KiB
C

/*
******************************************************************************
* 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 <roger@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include "ipssh.h"
#include "ipssh_h.h"
#include "ipssh_config.h"
#include <ipssh.h>
#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 <ipcom_clib.h>
#include <ipcom_syslog.h>
#include <ipcom_sock.h>
#include <ipcom_sock2.h>
#include <ipcom_buffer.h>
#include <ipcom_cstyle.h>
#include <ipcom_type.h>
#include <ipcom_syslog.h>
#include <ipcom_getopt.h>
#include <ipcom_err.h>
#include <ipcom_os.h>
#include <ipcom_shell.h>
#include <ipcom_sysvar.h>
#include <ipcom_ipd.h>
#include <openssl/pem.h>
#include <openssl/err.h>
/*
****************************************************************************
* 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 <shellHook.h>
#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; i<IPSSH_MAX_LISTEN_PORTS;i++)
{
ipcom_sprintf(buf, "ipssh.port%i", i+1);
if (ipssh_sysvar_get_as_int(buf, &srv_ctx->port[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(pos<len);
if (alg_ids->flags & 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_sock<map_entry->sock)
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; i<IPSSH_MAX_LISTEN_PORTS; i++)
{
if (srv_ctx->port[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<IPSSH_MAX_LISTEN_PORTS; i++)
{
if (lst_sock[i] == IP_INVALID_SOCKET)
break;
if (IP_FD_ISSET(lst_sock[i], tmp_read_set))
{
clt_sock = ipcom_accept(lst_sock[i], IP_NULL, IP_NULL);
if (IP_INVALID_SOCKET == clt_sock)
{
IPCOM_LOG1(ERR, "ipssh :: ipcom_accept() failed, errno=%d", ipcom_errno);
continue;
}
if (ipssh_get_no_clients() >= 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(<MSS) to be sent immediately.
*/
int enable_no_delay = 1;
ipcom_setsockopt(clt_sock, IP_IPPROTO_TCP, IP_TCP_NODELAY,
&enable_no_delay, sizeof(enable_no_delay));
ipssh_launch_server(clt_sock, srv_ctx);
}
}
#ifdef IPSSH_V6_MAPPED_V4_ADDR_NOT_SUPPORTED
if (IP_FD_ISSET(lst_sock2[i], tmp_read_set))
{
clt_sock = ipcom_accept(lst_sock2[i], IP_NULL, IP_NULL);
if (IP_INVALID_SOCKET == clt_sock)
{
IPCOM_LOG1(ERR, "ipssh :: ipcom_accept() failed, errno=%d", ipcom_errno);
continue;
}
if (ipssh_get_no_clients() >= 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; i<IPSSH_MAX_LISTEN_PORTS; i++)
{
if (lst_sock[i] != IP_INVALID_SOCKET)
{
ipcom_socketclose(lst_sock[i]);
lst_sock[i] = IP_INVALID_SOCKET;
}
#ifdef IPSSH_V6_MAPPED_V4_ADDR_NOT_SUPPORTED
if (lst_sock2[i] != IP_INVALID_SOCKET)
{
ipcom_socketclose(lst_sock2[i]);
lst_sock2[i] = IP_INVALID_SOCKET;
}
#endif
}
if (read_set)
{
ipcom_free(read_set);
read_set = IP_NULL;
}
if (tmp_read_set)
{
ipcom_free(tmp_read_set);
tmp_read_set = IP_NULL;
}
if(srv_ctx)
{
ipssh_srv_ctx_free(srv_ctx);
srv_ctx = IP_NULL;
}
if (restart)
{
restart = IP_FALSE;
goto restart;
}
if (fd_ipd == IP_INVALID_SOCKET)
(void)ipcom_ipd_init("ipssh", IPCOM_ERR_FAILED, &fd_ipd);
(void)ipcom_ipd_exit("ipssh", fd_ipd);
IPSSH_CODE_UNLOCK();
ipcom_proc_exit();
return;
}
/*
*===========================================================================
* ipssh_launch_server
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL void
ipssh_launch_server(Ip_fd sock, Ipssh_srv_ctx *srv_ctx)
{
char proc_name[IPCOM_PROC_NAME_MAX+1];
char addr_str[64];
union Ip_sockaddr_union addr;
Ip_socklen_t len = sizeof(union Ip_sockaddr_union);
Ip_pid_t pid;
Ipcom_ipc ipc;
Ipssh_spawn_args *spawn_args;
Ipcom_proc_attr proc_attr;
if (ipcom_getpeername(sock, &addr.sa, &len) != IPCOM_SUCCESS)
{
IPCOM_LOG1(ERR, "ipssh_launch_server :: ipcom_getpeername() failed, errno=%d", ipcom_errno);
ipcom_socketclose(sock);
return;
}
#ifdef IPCOM_USE_INET
if (addr.sa.sa_family == IP_AF_INET)
{
if (ipcom_inet_ntop(IP_AF_INET, &addr.sin.sin_addr, addr_str, sizeof(addr_str)) == IP_NULL)
{
IPCOM_LOG1(ERR, "ipssh_launch_server :: ipcom_inet_ntop failed(), errno=%d", ipcom_errno);
ipcom_socketclose(sock);
return;
}
}
#endif
#ifdef IPCOM_USE_INET6
if (addr.sa.sa_family == IP_AF_INET6)
{
if (ipcom_inet_ntop(IP_AF_INET6, &addr.sin6.sin6_addr, addr_str, sizeof(addr_str)) == IP_NULL)
{
IPCOM_LOG1(ERR, "ipssh_launch_server :: ipcom_inet_ntop failed(), errno=%d", ipcom_errno);
ipcom_socketclose(sock);
return;
}
}
#endif
#if IPCOM_PROC_NAME_MAX < 51
IP_PANIC();
#endif
ipcom_sprintf(proc_name, "ipssh_%s_%d_%d", addr_str, ip_ntohs(addr.sin.sin_port), g_ipssh.task_name_id);
g_ipssh.task_name_id++;
ipcom_proc_attr_init(&proc_attr);
proc_attr.flags |= IPCOM_PROC_FLAG_FP;
proc_attr.stacksize = IPCOM_PROC_STACK_MAX;
proc_attr.priority = IPSSH_SPAWN_PROC_PRIO;
if (ipcom_proc_acreate(proc_name, ipssh_server, &proc_attr, &pid) != IPCOM_SUCCESS)
{
IPCOM_LOG1(ERR, "ipssh_launch_server :: failed to start process, errno=%d", ipcom_errno);
ipcom_socketclose(sock);
return;
}
if (ipcom_ipc_open(&ipc, proc_name, IPC_TIMEOUT_INFINITE) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_launch_server :: ipcom_ipc_open() failed");
ipcom_socketclose(sock);
return;
}
spawn_args = ipcom_ipc_malloc(sizeof(Ipssh_spawn_args));
if (!spawn_args)
{
IPCOM_LOG0(ERR, "ipssh_launch_server :: out of memory");
(void)ipcom_ipc_close(&ipc);
ipcom_socketclose(sock);
return;
}
#ifdef IPSSH_CHOWN_SOCKET_CB
IPSSH_CHOWN_SOCKET_CB(sock, pid);
#endif
/* First send and receive a sync signal to avoid race condition. Any signal will do, let's
* use spawn_args */
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");
return;
}
if (ipcom_ipc_receive(IP_NULL, &spawn_args, IPC_TIMEOUT_INFINITE) != IPCOM_SUCCESS)
{
ipcom_socketclose(sock);
IPCOM_LOG0(ERR, "ipssh_launch_server :: ipcom_ipc_receive() failed");
return;
}
/* Now send spawn_args */
spawn_args->sock = 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));
i<pos && ctx;
ctx = IPCOM_LIST_NEXT(&(ctx->list)), 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
****************************************************************************
*/