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

2716 lines
84 KiB
C

/*
******************************************************************************
* SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipssh_transport.c,v $ $Revision: 1.97.10.14 $
* $Source: /home/interpeak/CVSRoot/ipssh/src/ipssh_transport.c,v $
* $Author: svc-cmnet $ $Date: 2018-09-12 23:36:06 $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Roger Boden <roger@interpeak.se>
******************************************************************************
*/
/*
modification history
--------------------
31jul18,l_y Wrong definition used when calculating the key
length for MAC algorithms in SSH. (VXW6-86850)
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#include "ipssh_h.h"
#include "ipssh_transport.h"
#include "ipssh_auth.h"
#include "ipssh_connection.h"
#include "ipssh_sftp.h"
#include "ipssh_list.h"
#include "ipssh_buffer.h"
#include "ipssh_keyex_dh.h"
#include <ipcom_clib.h>
#include <ipssh_config.h>
#include <ipcom_type.h>
#include <ipcom_cstyle.h>
#include <ipcom_buffer.h>
#include <ipcom_sock.h>
#include <ipcom_sock2.h>
#include "openssl/bn.h"
#include "openssl/hmac.h"
/*
****************************************************************************
* 4 EXTERN PROTOTYPES
****************************************************************************
*/
IP_GLOBAL Ip_err ipssh_send_msg_kexrsa_pubkey(Ipssh_conn* ssh_conn);
IP_GLOBAL Ip_err ipssh_do_keyex_rsa_clt(Ipssh_conn* ssh_conn);
/*
****************************************************************************
* 5 DEFINES
****************************************************************************
*/
/* IPv4/IPv6 macros */
#ifdef IPCOM_USE_INET6
#define IPSSH_SOCKADDRLEN(s) ((s)->sa.sa_family == IP_AF_INET ? \
sizeof((s)->sin) : sizeof((s)->sin6))
#else
#define IPSSH_SOCKADDRLEN(s) (sizeof((s)->sin))
#endif
#if !defined(IPSSH_USE_DES) && !defined(IPSSH_USE_CAST) && !defined(IPSSH_USE_BLOWFISH) && !defined(IPSSH_USE_ARCFOUR)
#error MUST_DEFINE_AT_LEAST_ONE_ENCRYPTION_ALGORITHM
#endif
#define IPSSH_COOKIE_SIZE 16
#define IPSSH_KEXINIT_GET_COOKIE(pkt) (&(pkt[6]))
#define IPSSH_KEXINIT_GET_KEX_ALGS(pkt) (&(pkt[22]))
#define IPSSH_CONN_CLOSED 1
#define IPSSH_CONN_NEED_MORE_DATA 2
/* Define if you want the negotiated keys to be included in debug printouts.
* Good for debugging purposes, REALLY bad from a security point of view.
* Only set when trying to debug failing handshakes.
#define IPSSH_KEY_DBG
*/
/* Define if you want the hex dumps of cleartext and encrypted packets to
* be included in debug printouts.
* Good for debugging purposes, REALLY bad from a security point of view.
* Only set when trying to debug.
#define IPSSH_PKT_DBG
*/
/*
****************************************************************************
* 6 TYPES
****************************************************************************
*/
struct Ipssh_v2_pkt_handler_map_st
{
int pkt_type;
Ipssh_pkt_handler handler;
int allowed_states;
};
typedef struct Ipssh_v2_pkt_handler_map_st Ipssh_v2_pkt_handler_map;
/* TODO: add handler for MSG_CHANNEL_FAILURE */
IP_STATIC Ipssh_v2_pkt_handler_map pkt_handlers[] =
{
{ IPSSH_MSG_CHANNEL_DATA, ipssh_msg_channel_data, IPSSH_CONN_STATE_SRV_TERM|IPSSH_CONN_STATE_SRV_SSH_SFTP|IPSSH_CONN_STATE_CLT_CONNECTED},
{ IPSSH_MSG_CHANNEL_SUCCESS, ipssh_msg_channel_success, IPSSH_CONN_STATE_SRV_TERM|IPSSH_CONN_STATE_CLT_CONNECTED},
{ IPSSH_MSG_CHANNEL_CLOSE, ipssh_msg_channel_close, IPSSH_CONN_STATE_SRV_TERM|IPSSH_CONN_STATE_SRV_SSH_SFTP|IPSSH_CONN_STATE_CLT_CONNECTED},
{ IPSSH_MSG_CHANNEL_EOF, ipssh_msg_channel_eof, IPSSH_CONN_STATE_SRV_TERM|IPSSH_CONN_STATE_SRV_SSH_SFTP},
{ IPSSH_MSG_CHANNEL_OPEN, ipssh_msg_channel_open, IPSSH_CONN_STATE_SRV_TERM | IPSSH_CONN_STATE_SRV_SSH_SFTP},
{ IPSSH_MSG_CHANNEL_OPEN_CONFIRMATION, ipssh_msg_channel_open_confirmation, IPSSH_CONN_STATE_SRV_TERM|IPSSH_CONN_STATE_CLT_CONNECTED},
{ IPSSH_MSG_CHANNEL_OPEN_FAILURE, ipssh_msg_channel_open_failure, IPSSH_CONN_STATE_SRV_TERM|IPSSH_CONN_STATE_CLT_CONNECTED},
{ IPSSH_MSG_CHANNEL_REQUEST, ipssh_msg_channel_request, IPSSH_CONN_STATE_SRV_TERM | IPSSH_CONN_STATE_SRV_SSH_SFTP },
{ IPSSH_MSG_CHANNEL_WINDOW_ADJUST, ipssh_msg_ch_win_adjust, IPSSH_CONN_STATE_SRV_TERM|IPSSH_CONN_STATE_SRV_SSH_SFTP|IPSSH_CONN_STATE_CLT_CONNECTED},
{ IPSSH_MSG_GLOBAL_REQUEST, ipssh_msg_global_request, IPSSH_CONN_STATE_SRV_TERM|IPSSH_CONN_STATE_SRV_SSH_SFTP|IPSSH_CONN_STATE_CLT_CONNECTED},
{ IPSSH_MSG_DISCONNECT, ipssh_msg_disconnect, IPSSH_CONN_STATE_ALL},
{ IPSSH_MSG_IGNORE, ipssh_msg_ignore, IPSSH_CONN_STATE_ALL},
{ IPSSH_MSG_DEBUG, ipssh_msg_ignore, IPSSH_CONN_STATE_ALL},
{ IPSSH_MSG_KEXDH_INIT, ipssh_msg_kex_dh_init, IPSSH_CONN_STATE_SRV_KEX_DH_INIT},
#ifdef IPSSH_USE_DH_GROUP_EX
{ IPSSH_MSG_KEX_DH_GEX_REQUEST_OLD, ipssh_msg_kex_dh_gex_request_old, IPSSH_CONN_STATE_SRV_KEX_GEX},
{ IPSSH_MSG_KEX_DH_GEX_REQUEST, ipssh_msg_kex_dh_gex_request, IPSSH_CONN_STATE_SRV_KEX_GEX},
{ IPSSH_MSG_KEX_DH_GEX_INIT, ipssh_msg_kex_dh_gex_init, IPSSH_CONN_STATE_SRV_KEX_GEX},
#endif
#ifdef IPSSH_USE_KEYEX_RSA
{ IPSSH_MSG_KEX_RSA_SECRET, ipssh_msg_kex_rsa_secret, IPSSH_CONN_STATE_SRV_KEX_RSA},
#endif
{ IPSSH_MSG_KEXINIT, ipssh_msg_kexinit, IPSSH_CONN_STATE_ALL},
{ IPSSH_MSG_NEWKEYS, ipssh_msg_new_key, IPSSH_CONN_STATE_SRV_NEW_KEY },
{ IPSSH_MSG_SERVICE_REQUEST, ipssh_msg_service_req, IPSSH_CONN_STATE_SERVICE_REQ },
{ IPSSH_MSG_SERVICE_ACCEPT, ipssh_msg_service_accept, IPSSH_CONN_STATE_ALL },
{ IPSSH_MSG_USERAUTH_REQUEST, ipssh_msg_userauth_req, IPSSH_CONN_STATE_SRV_USER_AUTH },
{ IPSSH_MSG_USERAUTH_FAILURE, ipssh_msg_userauth_failure, IPSSH_CONN_STATE_CLT_USER_AUTH },
{ IPSSH_MSG_USERAUTH_SUCCESS, ipssh_msg_userauth_success, IPSSH_CONN_STATE_CLT_USER_AUTH },
{ IPSSH_MSG_USERAUTH_BANNER, ipssh_msg_userauth_banner, IPSSH_CONN_STATE_ALL },
{ -1, IP_NULL, 0}
};
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
IP_STATIC Ip_err ipssh_parse_kexinit(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt);
IP_STATIC Ip_err ipssh_choose_algs(Ipssh_conn* ssh_conn);
IP_STATIC Ip_u8 ipssh_find_negotiated_option(Ip_u8* clt_algs, Ip_u8* srv_algs);
IP_STATIC Ip_err ipssh_compute_key(Ipssh_conn* ssh_conn, Ip_u8 id, Ipssh_key* key);
IP_STATIC const EVP_CIPHER* ipssh_get_evp_enc(Ip_u8 alg_id);
IP_STATIC Ip_err ipssh_get_evp_md(Ip_u8 alg_id, Ipssh_mac* mac);
IP_GLOBAL Ip_err ipssh_decrypt_pkt(Ipssh_conn* ssh_conn);
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
****************************************************************************
* 9 LOCAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipssh_copy_key
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_copy_key(Ipssh_key* dst, Ipssh_key* src)
{
if (dst->key)
ipcom_free(dst->key);
dst->key = ipcom_malloc(src->len);
if (!dst->key)
{
IPCOM_LOG1(ERR, "ipssh_copy_key :: failed to allocate key, len=%d", src->len);
return IPCOM_ERR_FAILED;
}
ipcom_memcpy(dst->key, src->key, src->len);
dst->len = src->len;
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_cipher_ctx_skip_bytes
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipssh_cipher_ctx_skip_bytes(EVP_CIPHER_CTX *ctx, int skip_len)
{
Ip_u8 dummy_in[16] = {0};
Ip_u8 dummy_out[32];
int in_len = sizeof(dummy_in);
int out_len = sizeof(dummy_out);
while (skip_len > 0)
{
out_len = sizeof(dummy_out);
in_len = IP_MIN(skip_len, (int)sizeof(dummy_in));
(void)EVP_CipherUpdate(ctx,dummy_out, &out_len, dummy_in, in_len);
skip_len -= in_len;
}
}
/*
*===========================================================================
* ipssh_setup_recv_bufs
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
#define MAX_TRIES 10000
IP_STATIC Ip_s32
ipssh_setup_recv_bufs(Ipssh_conn* ssh_conn)
{
Ip_u8 enc_buf[16];
Ip_u8 plain_buf[16];
Ip_u32 enc_buf_len;
Ip_u32 pkt_len;
Ip_u32 len;
Ip_u32 enc_len;
Ip_ssize_t rc;
Ip_ssize_t read_bytes;
int max_tries = MAX_TRIES;
int mod = 2;
enc_len = IP_MAX(ssh_conn->recv_ctx.cipher->block_size, 4);
ip_assert(enc_len <= (Ip_ssize_t) sizeof(enc_buf));
read_bytes = 0;
again:
rc = ipcom_socketread(ssh_conn->sock, enc_buf + read_bytes, enc_len - read_bytes);
if(rc == 0)
return IPSSH_CONN_CLOSED;
if(rc == -1)
{
if( (MAX_TRIES-max_tries)% mod == 0)
{
IPCOM_LOG2(DEBUG, "ipssh_setup_recv_bufs :: attempt: %d, errno: %d", MAX_TRIES-max_tries, ipcom_errno);
mod *= 2;
}
/* Some native stacks, e.g. VxWorks 6.2, return ETIMEOUT if a read on a non-blocking
* socket cannot complete */
if(ipcom_errno == IP_ERRNO_EWOULDBLOCK || ipcom_errno == IP_ERRNO_ETIMEDOUT)
{
max_tries--;
if(max_tries > 0)
{
ipcom_millisleep(10);
goto again;
}
}
IPCOM_LOG3(ERR, "ipssh_setup_recv_bufs :: failed to read length (%d bytes), rc: %d, errno: %d", enc_len, rc, ipcom_errno);
return IPCOM_ERR_FAILED;
}
if(rc + read_bytes != (Ip_ssize_t)enc_len)
{
read_bytes += rc;
ip_assert(read_bytes < (Ip_ssize_t)enc_len);
goto again;
}
if(EVP_DecryptUpdate(&(ssh_conn->recv_ctx), plain_buf, (int*)&len, enc_buf, enc_len) != 1)
return IPCOM_ERR_FAILED;
ip_assert(len == enc_len);
if(len != enc_len)
return IPCOM_ERR_FAILED;
pkt_len = IPSSH_GET_PKT_LEN(plain_buf);
if(pkt_len > IPSSH_MAX_PKT_SIZE || pkt_len < 8)
{
IPCOM_LOG1(WARNING, "ipssh_setup_recv_bufs :: illegal pkt len: %d", pkt_len);
return IPCOM_ERR_FAILED;
}
/* Calculate needed length for enc_buf. If mac is activited space is needed
* for mac value (not included in message length). A block already read and
* decrypted (4 bytes in case no encryption is used) */
if(ssh_conn->recv_mac.md)
{
int mac_len;
if(ssh_conn->recv_mac.use_bits != 0)
{
ip_assert(ssh_conn->recv_mac.use_bits % 8 == 0);
mac_len = ssh_conn->recv_mac.use_bits/8;
}
else
mac_len = ssh_conn->recv_mac.md->md_size;
enc_buf_len = pkt_len + 4 + mac_len - len;
}
else
enc_buf_len = pkt_len + 4 - len;
IPCOM_LOG2(DEBUG2, "ipssh_setup_recv_bufs :: plain buf len: %d, enc buf len:%d",
pkt_len+4, enc_buf_len);
if(ssh_conn->recv_plain_buf)
ipcom_buffer_free(ssh_conn->recv_plain_buf);
if(ssh_conn->recv_enc_buf)
ipcom_buffer_free(ssh_conn->recv_enc_buf);
if(!(ssh_conn->recv_plain_buf = ipcom_buffer_new(pkt_len+4)) ||
!(ssh_conn->recv_enc_buf = ipcom_buffer_new(enc_buf_len)))
{
IPCOM_LOG0(ERR, "ipssh_setup_recv_bufs :: out of memory");
return IPCOM_ERR_FAILED;
}
(void)ipcom_buffer_put(ssh_conn->recv_plain_buf, plain_buf, len);
return IPCOM_SUCCESS;
}
#ifdef IPSSH_DEBUG
IP_STATIC char* ipssh_get_msg_name(Ip_s32 pkt_type)
{
static char tmp_buf[32];
char* ret;
switch(pkt_type)
{
/* Message numbers, transport protocol */
case 1:
ret = "MSG_DISCONNECT";
break;
case 2:
ret = "MSG_IGNORE";
break;
case 3:
ret = "MSG_UNIMPLEMENTED";
break;
case 4:
ret = "MSG_DEBUG";
break;
case 5:
ret = "MSG_SERVICE_REQUEST";
break;
case 6:
ret = "MSG_SERVICE_ACCEPT";
break;
case 20:
ret = "MSG_KEXINIT";
break;
case 21:
ret = "MSG_NEWKEYS";
break;
case 30:
ret = "MSG_KEXDH_INIT";
break;
case 31:
ret = "MSG_KEXDH_REPLY";
break;
/* Message numbers, authentication protocol */
case 50:
ret = "MSG_USERAUTH_REQUEST";
break;
case 51:
ret = "MSG_USERAUTH_FAILURE";
break;
case 52:
ret = "MSG_USERAUTH_SUCCESS";
break;
case 53:
ret = "MSG_USERAUTH_BANNER";
break;
case 60:
ret = "MSG_USERAUTH_PASSWD_CHANGEREQ";
break;
/* Message numbers, connect protocol */
case 80:
ret = "MSG_GLOBAL_REQUEST";
break;
case 81:
ret = "MSG_REQUEST_SUCCESS";
break;
case 82:
ret = "MSG_REQUEST_FAILURE";
break;
case 90:
ret = "MSG_CHANNEL_OPEN";
break;
case 91:
ret = "MSG_CHANNEL_OPEN_CONFIRMATION";
break;
case 92:
ret = "MSG_CHANNEL_OPEN_FAILURE";
break;
case 93:
ret = "MSG_CHANNEL_WINDOW_ADJUST";
break;
case 94:
ret = "MSG_CHANNEL_DATA";
break;
case 95:
ret = "MSG_CHANNEL_EXTENDED_DATA";
break;
case 96:
ret = "MSG_CHANNEL_EOF";
break;
case 97:
ret = "MSG_CHANNEL_CLOSE";
break;
case 98:
ret = "MSG_CHANNEL_REQUEST";
break;
case 99:
ret = "MSG_CHANNEL_SUCCESS";
break;
case 100:
ret = "MSG_CHANNEL_FAILURE";
break;
/* Message numbers, sftp protocol */
default:
ipcom_memset(tmp_buf, 0, sizeof(tmp_buf));
ipcom_sprintf(tmp_buf, "%ld", pkt_type);
return tmp_buf;
}
return ret;
}
#endif
/*
*===========================================================================
* ipssh_compute_key
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_compute_key(Ipssh_conn* ssh_conn, Ip_u8 id, Ipssh_key* key)
{
EVP_MD_CTX md_ctx;
Ipcom_buffer* buf;
Ip_err rc = IPCOM_ERR_FAILED;
Ip_s32 gen_bytes = 0;
Ip_s32 md_size = 0;
IPCOM_LOG1(DEBUG2, "ipssh_compute_key :: id = %c", id);
if(key->len == 0)
return IPCOM_SUCCESS;
if(EVP_DigestInit(&md_ctx, ssh_conn->hash_func()) != 1)
return IPCOM_ERR_FAILED;
buf = ipcom_buffer_new(IP_MAX(ssh_conn->secr_len, key->len)+64);
while( key->alloc < key->len)
key->alloc += md_ctx.digest->md_size;
key->key = ipcom_malloc(key->alloc);
if(!buf || !key->key)
{
IPCOM_LOG1(ERR, "ipssh_compute_key :: out of memory, id=%c", id);
goto cleanup;
}
(void)ipssh_buffer_put_ssh_mpint(buf, ssh_conn->shared_secret, ssh_conn->secr_len);
md_size = md_ctx.digest->md_size;
gen_bytes += md_size;
/* Key = HASH(shared_secret | hash | "id" | session_id) */
if(!EVP_DigestUpdate(&md_ctx, ipcom_buffer_start_ptr(buf), ipcom_buffer_len(buf))
|| !EVP_DigestUpdate(&md_ctx, ssh_conn->hash, ssh_conn->hash_len)
|| !EVP_DigestUpdate(&md_ctx, &id, 1)
|| !EVP_DigestUpdate(&md_ctx, ssh_conn->sess_id, ssh_conn->sess_id_len)
|| !EVP_DigestFinal(&md_ctx, key->key, IP_NULL))
{
IPCOM_LOG0(ERR, "ipssh_compute_key :: EVP_DigestUpdate/Final failed");
goto cleanup;
}
/* Expand if necesary
* Kn = HASH(shared_secret | hash | K1 | ... | Kn-1)
* K = K1 | K2 | ... | Kn
*/
while( gen_bytes < key->len)
{
if(!EVP_DigestInit(&md_ctx, ssh_conn->hash_func())
|| !EVP_DigestUpdate(&md_ctx, ipcom_buffer_start_ptr(buf), ipcom_buffer_len(buf))
|| !EVP_DigestUpdate(&md_ctx, ssh_conn->hash, ssh_conn->hash_len)
|| !EVP_DigestUpdate(&md_ctx, key->key, gen_bytes)
|| !EVP_DigestFinal(&md_ctx, key->key + gen_bytes, IP_NULL))
{
IPCOM_LOG0(ERR, "ipssh_compute_key :: EVP_DigestUpdate/Final failed 2");
goto cleanup;
}
gen_bytes += md_size;
}
#if defined(IPSSH_DEBUG) && defined(IPSSH_KEY_DBG)
ipcom_debug_printf("key: %c\n", id);
ipcom_buffer_reset(buf);
ipcom_buffer_put(buf, key->key, key->len);
ipcom_buffer_dump(buf);
#endif
rc = IPCOM_SUCCESS;
cleanup:
if(buf)
ipcom_buffer_free(buf);
(void)EVP_MD_CTX_cleanup(&md_ctx);
return rc;
}
/*
*===========================================================================
* ipssh_get_evp_enc
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC const EVP_CIPHER*
ipssh_get_evp_enc(Ip_u8 alg_id)
{
switch(alg_id)
{
#ifdef IPSSH_USE_AES
case IPSSH_ENC_AES128_CBC_ID:
return EVP_aes_128_cbc();
case IPSSH_ENC_AES192_CBC_ID:
return EVP_aes_192_cbc();
case IPSSH_ENC_AES256_CBC_ID:
return EVP_aes_256_cbc();
#endif
#ifdef IPSSH_USE_DES
case IPSSH_ENC_3DES_CBC_ID:
return EVP_des_ede3_cbc();
case IPSSH_ENC_DES_CBC_ID:
return EVP_des_cbc();
#endif
#ifdef IPSSH_USE_BLOWFISH
case IPSSH_ENC_BLOWFISH_CBC_ID:
return EVP_bf_cbc();
#endif
#ifdef IPSSH_USE_CAST
case IPSSH_ENC_CAST128_CBC_ID:
return EVP_cast5_cbc();
#endif
#ifdef IPSSH_USE_ARCFOUR
case IPSSH_ENC_ARCFOUR_ID:
case IPSSH_ENC_ARCFOUR_128_ID:
return EVP_rc4();
#endif
case IPSSH_ENC_NONE_ID:
return EVP_enc_null();
default:
IPCOM_LOG1(ERR, "ipssh_get_evp_enc :: unknown alg id: %d", alg_id);
IP_PANIC();
}
return IP_NULL;
}
/*
*===========================================================================
* ipssh_get_evp_md
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_get_evp_md(Ip_u8 alg_id, Ipssh_mac* mac)
{
#ifdef OPENSSL_FIPS
int fips_mode = FIPS_mode();
#endif
switch(alg_id)
{
#ifdef IPSSH_USE_SHA1
case IPSSH_MAC_SHA1_ID:
mac->md = EVP_sha1();
mac->use_bits = 0;
break;
case IPSSH_MAC_SHA1_96_ID:
mac->md = EVP_sha1();
mac->use_bits = 96;
break;
#endif
#ifdef _WRS_CONFIG_COMPONENT_IPSSH_SUPPORT_SHA2
#ifdef IPSSH_USE_SHA256
case IPSSH_MAC_SHA256_ID:
mac->md = EVP_sha256();
mac->use_bits = 0;
break;
#endif
#ifdef IPSSH_USE_SHA512
case IPSSH_MAC_SHA512_ID:
mac->md = EVP_sha512();
mac->use_bits = 0;
break;
#endif
#endif
#ifdef IPSSH_USE_MD5
case IPSSH_MAC_MD5_ID:
#ifdef OPENSSL_FIPS
if (fips_mode)
{
IPCOM_LOG0(INFO, "ipssh_get_evp_md :: MD5 not allowed in FIPS mode");
return IPCOM_ERR_FAILED;
}
#endif
mac->md = EVP_md5();
mac->use_bits = 0;
break;
case IPSSH_MAC_MD5_96_ID:
mac->md = EVP_md5();
mac->use_bits = 96;
break;
#endif
case IPSSH_MAC_NONE_ID:
mac->md= EVP_md_null();
mac->use_bits = 0;
break;
default:
IPCOM_LOG1(ERR, "ipssh_get_evp_md :: unknown alg id: %d", alg_id);
IP_PANIC();
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_set_key_lengths
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_set_key_length(Ip_u8 alg_id, Ipssh_key* key)
{
switch(alg_id)
{
#ifdef IPSSH_USE_SHA1
case IPSSH_MAC_SHA1_ID:
case IPSSH_MAC_SHA1_96_ID:
key->len = 20;
break;
#endif
#ifdef _WRS_CONFIG_COMPONENT_IPSSH_SUPPORT_SHA2
#ifdef IPSSH_USE_SHA256
case IPSSH_MAC_SHA256_ID:
key->len = 32;
break;
#endif
#ifdef IPSSH_USE_SHA512
case IPSSH_MAC_SHA512_ID:
key->len = 64;
break;
#endif
#endif
#ifdef IPSSH_USE_MD5
case IPSSH_MAC_MD5_ID:
case IPSSH_MAC_MD5_96_ID:
key->len = 16;
break;
#endif
case IPSSH_MAC_NONE_ID:
key->len = 0;
break;
default:
IPCOM_LOG1(ERR, "ipssh_set_key_length :: unknown algorithm: %d", alg_id);
IP_PANIC();
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_choose_algs
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_choose_algs(Ipssh_conn* ssh_conn)
{
const EVP_CIPHER* evp_cipher;
Ipssh_conn_prop* clt_proposal;
Ipssh_conn_prop* srv_proposal;
ip_assert(ssh_conn->mode == IPSSH_MODE_CLT || ssh_conn->mode == IPSSH_MODE_SRV);
if(ssh_conn->mode == IPSSH_MODE_CLT)
{
clt_proposal = &ssh_conn->config.v2_algs;
srv_proposal = &ssh_conn->peer_proposal;
}
else
{
clt_proposal = &ssh_conn->peer_proposal;
srv_proposal = &ssh_conn->config.v2_algs;
}
/* kex exchange algorithm */
ssh_conn->conn_prop_pending.kex_alg[0] =
ipssh_find_negotiated_option(clt_proposal->kex_alg, srv_proposal->kex_alg);
if(ssh_conn->conn_prop_pending.kex_alg[0] == IPSSH_ID_ERR)
{
IPCOM_LOG0(ERR, "ipssh_choose_algs :: no common key exchange algorithm found");
return IPCOM_ERR_FAILED;
}
if(ssh_conn->conn_prop_pending.kex_alg[0] == IPSSH_KEYEX_DH_GR1_SHA1_ID ||
ssh_conn->conn_prop_pending.kex_alg[0] == IPSSH_KEYEX_DH_GR14_SHA1_ID)
{
ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_KEX_DH_INIT;
ssh_conn->hash_func = EVP_sha1;
}
#ifdef IPSSH_USE_DH_GROUP_EX
else if(ssh_conn->conn_prop_pending.kex_alg[0] == IPSSH_KEYEX_DH_GEX_SHA1_ID)
{
ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_KEX_GEX;
ssh_conn->hash_func = EVP_sha1;
}
else if(ssh_conn->conn_prop_pending.kex_alg[0] == IPSSH_KEYEX_DH_GEX_SHA256_ID)
{
ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_KEX_GEX;
ssh_conn->hash_func = EVP_sha256;
}
#endif
else if(ssh_conn->conn_prop_pending.kex_alg[0] == IPSSH_KEYEX_RSA1024_SHA1_ID)
{
ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_KEX_RSA;
ssh_conn->hash_func = EVP_sha1;
}
else if(ssh_conn->conn_prop_pending.kex_alg[0] == IPSSH_KEYEX_RSA2048_SHA256_ID)
{
ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_KEX_RSA;
ssh_conn->hash_func = EVP_sha256;
}
else
{
IPCOM_LOG1(ERR, "ipssh_choose_algs :: unknown key exchange algorithm: %d",
ssh_conn->conn_prop_pending.kex_alg[0]);
return IPCOM_ERR_FAILED;
}
/* server host key algorithm */
ssh_conn->conn_prop_pending.srv_host_key_alg[0] =
ipssh_find_negotiated_option(clt_proposal->srv_host_key_alg, srv_proposal->srv_host_key_alg);
if(ssh_conn->conn_prop_pending.srv_host_key_alg[0] == IPSSH_ID_ERR)
{
IPCOM_LOG0(ERR, "ipssh_choose_algs :: no common server key algorithm found");
return IPCOM_ERR_FAILED;
}
/* encryption algorithm, clt 2 srv */
ssh_conn->conn_prop_pending.enc_alg_clt2srv[0] =
ipssh_find_negotiated_option(clt_proposal->enc_alg_clt2srv, srv_proposal->enc_alg_clt2srv);
if(ssh_conn->conn_prop_pending.enc_alg_clt2srv[0] == IPSSH_ID_ERR)
{
IPCOM_LOG0(ERR, "ipssh_choose_algs :: no common encr algorithm found, clt 2 srv");
return IPCOM_ERR_FAILED;
}
evp_cipher = ipssh_get_evp_enc(ssh_conn->conn_prop_pending.enc_alg_clt2srv[0]);
if(!evp_cipher)
{
IPCOM_LOG1(ERR, "ipssh_choose_algs :: ipssh_get_evp_enc failed for enc alg, clt to srv: %d", ssh_conn->conn_prop_pending.enc_alg_clt2srv[0]);
IP_PANIC();
return IPCOM_ERR_FAILED;
}
ssh_conn->key_mtrl.key_c2s.len = evp_cipher->key_len;
ssh_conn->key_mtrl.iv_c2s.len = evp_cipher->iv_len;
/* encryption algorithm, srv 2 clt */
ssh_conn->conn_prop_pending.enc_alg_srv2clt[0] =
ipssh_find_negotiated_option(clt_proposal->enc_alg_srv2clt, srv_proposal->enc_alg_srv2clt);
if(ssh_conn->conn_prop_pending.enc_alg_srv2clt[0] == IPSSH_ID_ERR)
{
IPCOM_LOG0(ERR, "ipssh_choose_algs :: no common encr algorithm found, srv 2 clt");
return IPCOM_ERR_FAILED;
}
evp_cipher = ipssh_get_evp_enc(ssh_conn->conn_prop_pending.enc_alg_srv2clt[0]);
if(!evp_cipher)
{
IPCOM_LOG1(ERR, "ipssh_choose_algs :: ipssh_get_evp_enc failed for enc alg, srv to clt: %s", ssh_conn->conn_prop_pending.enc_alg_srv2clt);
IP_PANIC();
return IPCOM_ERR_FAILED;
}
ssh_conn->key_mtrl.key_s2c.len = evp_cipher->key_len;
ssh_conn->key_mtrl.iv_s2c.len = evp_cipher->iv_len;
/* mac algorithm, clt 2 srv */
ssh_conn->conn_prop_pending.mac_alg_clt2srv[0] =
ipssh_find_negotiated_option(clt_proposal->mac_alg_clt2srv, srv_proposal->mac_alg_clt2srv);
if(ssh_conn->conn_prop_pending.mac_alg_clt2srv[0] == IPSSH_ID_ERR)
{
IPCOM_LOG0(ERR, "ipssh_choose_algs :: no common mac algorithm found, clt 2 srv");
return IPCOM_ERR_FAILED;
}
if(ipssh_set_key_length(ssh_conn->conn_prop_pending.mac_alg_clt2srv[0],
&(ssh_conn->key_mtrl.mac_key_c2s)))
{
IPCOM_LOG0(ERR, "ipssh_choose_algs :: ipssh_set_key_length failed for mac alg, clt to srv");
return IPCOM_ERR_FAILED;
}
/* mac algorithm, srv 2 clt */
ssh_conn->conn_prop_pending.mac_alg_srv2clt[0] =
ipssh_find_negotiated_option(clt_proposal->mac_alg_srv2clt, srv_proposal->mac_alg_srv2clt);
if(ssh_conn->conn_prop_pending.mac_alg_srv2clt[0] == IPSSH_ID_ERR)
{
IPCOM_LOG0(ERR, "ipssh_choose_algs :: no common encr algorithm found, srv 2 clt");
return IPCOM_ERR_FAILED;
}
if(ipssh_set_key_length(ssh_conn->conn_prop_pending.mac_alg_srv2clt[0],
&(ssh_conn->key_mtrl.mac_key_s2c)))
{
IPCOM_LOG0(ERR, "ipssh_choose_algs :: ipssh_set_key_length failed for mac alg, srv to clt");
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_find_negotiated_option
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_u8
ipssh_find_negotiated_option(Ip_u8* clt_algs, Ip_u8* srv_algs)
{
Ip_u8* clt_id;
Ip_u8* srv_id;
for(clt_id = clt_algs;*clt_id != IPSSH_ID_END; clt_id++)
{
for(srv_id = srv_algs; *srv_id != IPSSH_ID_END; srv_id++)
{
if(*clt_id == *srv_id)
return *clt_id;
}
}
return IPSSH_ID_ERR;
}
/*
*===========================================================================
* ipssh_parse_kexinit
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_parse_kexinit(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt)
{
char* str;
Ip_u32 dummy;
IPCOM_LOG0(DEBUG2, "ipssh_parse_kexinit()");
if(ipcom_buffer_get(pkt, ssh_conn->v2_cookie, IPSSH_COOKIE_SIZE) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_parse_kexinit :: failed unpack cookie");
return IPCOM_ERR_FAILED;
}
/* kex_algorithms */
str = ipssh_buffer_get_sshstr(pkt);
if(!str || ipssh_alg_list_str2id(str, ssh_conn->peer_proposal.kex_alg, (int) sizeof(ssh_conn->peer_proposal.kex_alg)) != IPCOM_SUCCESS)
{
IPCOM_LOG1(ERR, "ipssh_parse_kexinit :: failed to parse kex_alg: %s", str ? str : "");
if(str)
ipcom_free(str);
return IPCOM_ERR_FAILED;
}
IPCOM_LOG1(DEBUG2, "ipssh_parse_kexinit :: kex algs: %s", str);
ipcom_free(str);
/* server_host_key_algorithms */
str = ipssh_buffer_get_sshstr(pkt);
if(!str || ipssh_alg_list_str2id(str, ssh_conn->peer_proposal.srv_host_key_alg, (int) sizeof(ssh_conn->peer_proposal.srv_host_key_alg)) != IPCOM_SUCCESS)
{
IPCOM_LOG1(ERR, "ipssh_parse_kexinit :: failed to parse srv_host_key_algs: %s", str ? str : "");
if(str)
ipcom_free(str);
return IPCOM_ERR_FAILED;
}
IPCOM_LOG1(DEBUG2, "ipssh_parse_kexinit :: srv host key algs: %s", str);
ipcom_free(str);
/* encr algs, clt 2 srv */
str = ipssh_buffer_get_sshstr(pkt);
if(!str || ipssh_alg_list_str2id(str, ssh_conn->peer_proposal.enc_alg_clt2srv, (int) sizeof(ssh_conn->peer_proposal.enc_alg_clt2srv)) != IPCOM_SUCCESS)
{
IPCOM_LOG1(ERR, "ipssh_parse_kexinit :: failed to parse enc_algs_clt2srv:%s", str ? str : "");
if(str)
ipcom_free(str);
return IPCOM_ERR_FAILED;
}
IPCOM_LOG1(DEBUG2, "ipssh_parse_kexinit :: clt 2 srv enc algs: %s", str);
ipcom_free(str);
/* encr algs, srv 2 clt */
str = ipssh_buffer_get_sshstr(pkt);
if(!str || ipssh_alg_list_str2id(str, ssh_conn->peer_proposal.enc_alg_srv2clt, (int) sizeof(ssh_conn->peer_proposal.enc_alg_srv2clt)) != IPCOM_SUCCESS)
{
IPCOM_LOG1(ERR, "ipssh_parse_kexinit :: failed to parse enc_algs_srv2clt: %s", str ? str : "");
if(str)
ipcom_free(str);
return IPCOM_ERR_FAILED;
}
IPCOM_LOG1(DEBUG2, "ipssh_parse_kexinit :: srv 2 clt enc algs: %s", str);
ipcom_free(str);
/* mac algs, clt 2 srv */
str = ipssh_buffer_get_sshstr(pkt);
if(!str || ipssh_alg_list_str2id(str, ssh_conn->peer_proposal.mac_alg_clt2srv, (int) sizeof(ssh_conn->peer_proposal.mac_alg_clt2srv)) != IPCOM_SUCCESS)
{
IPCOM_LOG1(ERR, "ipssh_parse_kexinit :: failed to parse mac_algs_clt2srv: %s", str ? str : "");
if(str)
ipcom_free(str);
return IPCOM_ERR_FAILED;
}
IPCOM_LOG1(DEBUG2, "ipssh_parse_kexinit :: clt 2 srv mac algs: %s", str);
ipcom_free(str);
/* mac algs, srv 2 clt */
str = ipssh_buffer_get_sshstr(pkt);
if(!str || ipssh_alg_list_str2id(str, ssh_conn->peer_proposal.mac_alg_srv2clt, (int) sizeof(ssh_conn->peer_proposal.mac_alg_srv2clt)) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_parse_kexinit :: ipssh_str2list() failed for mac_algs_srv2clt");
if(str)
ipcom_free(str);
return IPCOM_ERR_FAILED;
}
IPCOM_LOG1(DEBUG2, "ipssh_parse_kexinit :: srv 2 clt mac algs: %s", str);
ipcom_free(str);
/* compression algs, clt 2 srv */
str = ipssh_buffer_get_sshstr(pkt);
if(!str || ipssh_alg_list_str2id(str, ssh_conn->peer_proposal.compr_alg_clt2srv, (int) sizeof(ssh_conn->peer_proposal.compr_alg_clt2srv)) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_parse_kexinit :: ipssh_str2list() failed for compr_algs_clt2srv");
if(str)
ipcom_free(str);
return IPCOM_ERR_FAILED;
}
IPCOM_LOG1(DEBUG2, "ipssh_parse_kexinit :: clt 2 srv compr algs: %s", str);
ipcom_free(str);
/* compression algs, srv 2 clt */
str = ipssh_buffer_get_sshstr(pkt);
if(!str || ipssh_alg_list_str2id(str, ssh_conn->peer_proposal.compr_alg_clt2srv, (int) sizeof(ssh_conn->peer_proposal.compr_alg_srv2clt)) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_parse_kexinit :: ipssh_str2list() failed for compr_algs_srv2clt");
if(str)
ipcom_free(str);
return IPCOM_ERR_FAILED;
}
IPCOM_LOG1(DEBUG2, "ipssh_parse_kexinit :: srv 2 clt compr algs: %s", str);
ipcom_free(str);
/* languages, clt 2 srv. Ignore for now */
str = ipssh_buffer_get_sshstr(pkt);
if(!str)
{
IPCOM_LOG0(ERR, "ipssh_parse_kexinit :: ipssh_str2list() failed for lang_clt2srv");
return IPCOM_ERR_FAILED;
}
ipcom_free(str);
/* languanges, srv 2 clt */
str = ipssh_buffer_get_sshstr(pkt);
if(!str)
{
IPCOM_LOG0(ERR, "ipssh_parse_kexinit :: ipssh_str2list() failed for lang_srv2clt");
return IPCOM_ERR_FAILED;
}
ipcom_free(str);
if(ipcom_buffer_get(pkt, &ssh_conn->first_kex_pkt_follows, 1) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_parse_kexinit :: failed to read first_key_pkt_follows");
return IPCOM_ERR_FAILED;
}
if(ipssh_buffer_get_uint(pkt, &dummy) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_parse_kexinit :: failed to read reserved int");
return IPCOM_ERR_FAILED;
}
if(ipssh_choose_algs(ssh_conn))
{
IPCOM_LOG0(ERR, "ipssh_parse_kexinit :: ipssh_choose_algs() failed");
return IPCOM_ERR_FAILED;
}
if(ssh_conn->first_kex_pkt_follows)
{
/* Check if the guessed algorithms are correct */
if(ssh_conn->peer_proposal.kex_alg[0] != ssh_conn->conn_prop_pending.kex_alg[0])
ssh_conn->skip_guessed_kex_pkt = 1;
if(ssh_conn->peer_proposal.srv_host_key_alg[0] != ssh_conn->conn_prop_pending.srv_host_key_alg[0])
ssh_conn->skip_guessed_kex_pkt = 1;
}
#ifdef IPSSH_USE_KEYEX_RSA
/* Check if KEX_RSA is negotiated. If so call init function */
if ((ssh_conn->conn_prop_pending.kex_alg[0] == IPSSH_KEYEX_RSA1024_SHA1_ID ||
ssh_conn->conn_prop_pending.kex_alg[0] == IPSSH_KEYEX_RSA2048_SHA256_ID) &&
ssh_conn->mode == IPSSH_MODE_SRV)
{
return ipssh_send_msg_kexrsa_pubkey(ssh_conn);
}
#endif
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_pkt_dispatcher
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_pkt_dispatch(Ipssh_conn* ssh_conn)
{
Ipssh_v2_pkt_handler_map* pkt_handler_map;
Ip_u8* pkt;
Ip_u8 pkt_type,pad_len;
Ip_u32 pkt_len;
pkt = ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf);
pkt_type = IPSSH_GET_PKT_TYPE(pkt);
pad_len = IPSSH_GET_PKT_PAD_LEN(pkt);
pkt_len = IPSSH_GET_PKT_LEN(pkt);
#ifdef IPSSH_DEBUG
{
char* msg_name;
msg_name = ipssh_get_msg_name(pkt_type);
IPCOM_LOG4(DEBUG2, "ipssh_pkt_dispatch :: dispatching packet:"
"type %s (%d), length: %d, pad length: %d",
msg_name?msg_name:"", pkt_type, pkt_len, pad_len);
} /*lint !e550*/
#endif
if (pad_len < 4)
{
IPCOM_LOG1(DEBUG2,"ipssh_pkt_dispatch :: bad packet padding length =%d",pad_len);
return IPCOM_ERR_FAILED;
}
if (pad_len+1 > pkt_len)
{
IPCOM_LOG2(DEBUG2,"ipssh_pkt_dispatch :: bad packet, packet length =%d,packett padding length =%d",
pkt_len, pad_len);
return IPCOM_ERR_FAILED;
}
for(pkt_handler_map = pkt_handlers; pkt_handler_map->handler; pkt_handler_map++)
{
if(pkt_handler_map->pkt_type != pkt_type)
continue;
if(pkt_handler_map->allowed_states & ssh_conn->conn_state)
return pkt_handler_map->handler(ssh_conn);
}
IPCOM_LOG2(ERR, "ipssh_pkt_dispatch :: received unexpected msg %d, in state 0x%x",
pkt_type, ssh_conn->conn_state);
return IPCOM_ERR_FAILED;
}
/*
****************************************************************************
* 10 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipssh_setup_clt_transport
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_setup_clt_transport(Ipssh_conn* ssh_conn, const char* hostname, Ip_u16 port)
{
struct Ip_addrinfo* addrinfo = IP_NULL;
char ver_str[255];
Ip_size_t str_len;
Ip_err rc = IPCOM_ERR_FAILED;
union Ip_sockaddr_union bindsa;
char *bindaddress = IP_NULL;
Ip_bool validAddr = IP_TRUE;
ipcom_memset(ver_str, 0, sizeof(ver_str));
if(ipcom_getaddrinfo(hostname, IP_NULL, IP_NULL, &addrinfo) != IPCOM_SUCCESS)
{
IPCOM_LOG1(WARNING, "ipssh_setup_clt_transport, ipcom_getaddrinfo failed, errno=%d", ipcom_errno);
goto cleanup;
}
ssh_conn->sock = ipcom_socket(addrinfo->ai_family, IP_SOCK_STREAM, 0);
if(ssh_conn->sock == IP_INVALID_SOCKET)
{
IPCOM_LOG1(WARNING, "ipssh_setup_clt_transport, ipcom_socket failed, errno=%d", ipcom_errno);
goto cleanup;
}
((union Ip_sockaddr_union*)addrinfo->ai_addr)->sin.sin_port = ip_htons(port);
bindaddress = ipcom_sysvar_get("ipsftpc.bindaddress", IP_NULL, IP_NULL);
if (bindaddress != IP_NULL)
{
IPCOM_LOG1(INFO,"ipssh_setup_clt_transport :: ipsftpc.bindaddress=%s", bindaddress);
ipcom_memset(&bindsa, 0, sizeof(bindsa));
bindsa.sa.sa_family = IP_AF_INET;
bindsa.sin.sin_len = sizeof(struct Ip_sockaddr_in);
if (ipcom_inet_pton (IP_AF_INET, bindaddress, &bindsa.sin.sin_addr) != 1)
{
#ifdef IPCOM_USE_INET6
bindsa.sa.sa_family = IP_AF_INET6;
bindsa.sin6.sin6_len = sizeof(struct Ip_sockaddr_in6);
if (ipcom_inet_pton(IP_AF_INET6, bindaddress, &bindsa.sin6.sin6_addr) != 1)
validAddr = IP_FALSE;
#endif
}
if (validAddr == IP_TRUE)
{
if (ipcom_bind(ssh_conn->sock, &bindsa.sa,
IPSSH_SOCKADDRLEN(&bindsa)) < 0)
{
IPCOM_LOG2(ERR,"ipssh_setup_clt_transport :: bind(%s) failed, errno=%d", bindaddress, ipcom_errno);
validAddr = IP_FALSE;
}
else
IPCOM_LOG1(INFO,"ipssh_setup_clt_transport :: ipcom_bind %s OK", bindaddress);
}
ipcom_free(bindaddress);
}
if(ipcom_connect(ssh_conn->sock, addrinfo->ai_addr, (Ip_socklen_t)addrinfo->ai_addrlen) != IPCOM_SUCCESS)
{
IPCOM_LOG1(WARNING, "ipssh_setup_clt_transport, ipcom_connect() failed, errno=%d", ipcom_errno);
goto cleanup;
}
/* Send version string */
if(ipssh_make_version_str(0, 1, ver_str, sizeof(ver_str)) != IPCOM_SUCCESS)
{
IPCOM_LOG0(WARNING, "ipssh_setup_clt_transport, ipssh_make_version_str() failed");
goto cleanup;
}
if(ipcom_send(ssh_conn->sock, ver_str, ipcom_strlen(ver_str), 0) != (int)ipcom_strlen(ver_str))
{
IPCOM_LOG1(WARNING, "ipssh_setup_clt_transport, ipcom_send() failed, errno=%d", ipcom_errno);
goto cleanup;
}
/* Remove trailing \r\n, not to be included in digital signature */
str_len = ipcom_strlen(ver_str);
while(str_len > 0 && (ver_str[str_len-1] == '\r' || ver_str[str_len-1] == '\n'))
{
str_len--;
ver_str[str_len] = 0;
}
if(ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), IPSSH_STR, (Ip_u8*)ver_str, str_len, 0) != IPCOM_SUCCESS)
{
IPCOM_LOG0(WARNING, "ipssh_setup_clt_transport, ipssh_msg2list() failed");
goto cleanup;
}
/* Read peer's version string */
ipcom_memset(ver_str, 0, sizeof(ver_str));
str_len = sizeof(ver_str) - 1;
if(ipssh_read_from_socket(ssh_conn->sock, ver_str, &str_len, "\n") != IPCOM_SUCCESS)
{
IPCOM_LOG1(WARNING, "ipssh_setup_clt_transport, ipssh_read_from_socket() failed, errno=%d", ipcom_errno);
goto cleanup;
}
/* Remove trailing \r\n */
ver_str[str_len] = 0;
while(str_len > 0 && (ver_str[str_len-1] == '\r' || ver_str[str_len-1] == '\n'))
{
str_len--;
ver_str[str_len] = 0;
}
if(str_len < 9)
{
IPCOM_LOG1(WARNING, "ipssh_setup_clt_transport, too short version string received, %d bytes", str_len);
goto cleanup;
}
if(ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), IPSSH_STR, (Ip_u8*)ver_str, ipcom_strlen(ver_str), 1) != IPCOM_SUCCESS)
{
IPCOM_LOG0(WARNING, "ipssh_setup_clt_transport, ipssh_msg2list() failed");
goto cleanup;
}
/* Parse version string */
{
Ip_s32 count, proto_ver_major, proto_ver_minor;
count = ipcom_sscanf(ver_str, "SSH-%d.%d",
(int*)&proto_ver_major, (int*)&proto_ver_minor);
if(count != 2)
{
IPCOM_LOG0(ERR, "ipssh_setup_clt_transport :: failed to parse version str");
return IPCOM_ERR_FAILED;
}
switch(ipssh_get_conn_version(proto_ver_major, proto_ver_minor, 0, 1, IP_TRUE))
{
case 2:
break;
default:
IPCOM_LOG2(ERR, "ipssh_setup_clt_transport :: bad version, %d.%d", proto_ver_major, proto_ver_minor);
goto cleanup;
}
}
if(ipssh_send_kexinit(ssh_conn) != IPCOM_SUCCESS)
goto cleanup;
if(ipssh_read_v2_pkt(ssh_conn) != IPCOM_SUCCESS)
goto cleanup;
if(ipssh_msg_kexinit(ssh_conn) != IPCOM_SUCCESS)
goto cleanup;
ipssh_reset_buffers(ssh_conn);
switch(ssh_conn->conn_prop_pending.kex_alg[0])
{
case IPSSH_KEYEX_DH_GR1_SHA1_ID:
case IPSSH_KEYEX_DH_GR14_SHA1_ID:
if(ipssh_do_keyex_dh_clt(ssh_conn) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_setup_clt_transport :: ipssh_do_keyex_dh_clt() failed");
goto cleanup;
}
break;
#ifdef IPSSH_USE_DH_GROUP_EX
case IPSSH_KEYEX_DH_GEX_SHA1_ID:
case IPSSH_KEYEX_DH_GEX_SHA256_ID:
if(ipssh_do_keyex_dh_gex_clt(ssh_conn) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_setup_clt_transport :: ipssh_do_keyex_dh_gex_clt() failed");
goto cleanup;
}
break;
#endif
#ifdef IPSSH_USE_KEYEX_RSA
case IPSSH_KEYEX_RSA2048_SHA256_ID:
case IPSSH_KEYEX_RSA1024_SHA1_ID:
if(ipssh_do_keyex_rsa_clt(ssh_conn) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_setup_clt_transport :: ipssh_do_keyex_rsa_clt() failed");
goto cleanup;
}
break;
#endif
default:
IP_PANIC();
goto cleanup;
}
rc = ipssh_do_user_auth_clt(ssh_conn);
if(rc != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_setup_clt_transport :: ipssh_do_user_auth_clt() failed");
goto cleanup;
}
rc = IPCOM_SUCCESS;
cleanup:
if(addrinfo)
ipcom_freeaddrinfo(addrinfo);
return rc;
}
/*
*===========================================================================
* ipssh_msg_disconnect
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_disconnect(Ipssh_conn* ssh_conn)
{
char* descr = IP_NULL, *lang_tag= IP_NULL;
Ip_u32 reason = 0;
/* Unpack message */
if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6)
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &reason)
|| !(descr = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf))
|| !(lang_tag = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)))
{
IPCOM_LOG0(DEBUG2, "ipssh_msg_disconnect :: failed to unpack message");
}
else
{
IPCOM_LOG3(DEBUG, "ipssh_msg_disconnect :: reason: %d, descr: %s, lang tag: %s",
reason, descr, lang_tag);
}
if(descr)
ipcom_free(descr);
if(lang_tag)
ipcom_free(lang_tag);
return IPCOM_ERR_FAILED; /* Tear down connection */
}
/*
*===========================================================================
* ipssh_msg_ignore
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_ignore(Ipssh_conn* ssh_conn)
{
#ifdef IPSSH_DEBUG_PRINTF
{
Ip_u8* pkt;
pkt = ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf);
IPCOM_LOG2(DEBUG, "ipssh_msg_ignore :: pkt type=%d, len=%d",
IPSSH_GET_PKT_TYPE(pkt), IPSSH_GET_PKT_LEN(pkt));
}
#endif
(void)ipssh_consume_pkt(ssh_conn->recv_plain_buf);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_send_service_accept
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_service_accept(Ipssh_conn* ssh_conn, const char* service_name)
{
Ipcom_buffer* pkt;
pkt = ipssh_buffer_new_v2_pkt(32 + ipcom_strlen(service_name), IPSSH_MSG_SERVICE_ACCEPT);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_service_accept :: out of memory");
return IPCOM_ERR_FAILED;
}
(void)ipssh_buffer_put_sshstr(pkt, service_name);
if(ipssh_send_pkt(ssh_conn, pkt, IP_TRUE))
{
IPCOM_LOG0(ERR, "ipssh_send_service_accept :: ipssh_send_pkt() failed");
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_send_service_request
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_service_request(Ipssh_conn* ssh_conn, const char* service_name)
{
Ipcom_buffer* pkt;
pkt = ipssh_buffer_new_v2_pkt(32 + ipcom_strlen(service_name), IPSSH_MSG_SERVICE_REQUEST);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_service_request :: out of memory");
return IPCOM_ERR_FAILED;
}
(void)ipssh_buffer_put_sshstr(pkt, service_name);
if(ipssh_send_pkt(ssh_conn, pkt, IP_TRUE))
{
IPCOM_LOG0(ERR, "ipssh_send_service_request :: ipssh_send_pkt() failed");
return IPCOM_ERR_FAILED;
}
ssh_conn->conn_state = IPSSH_CONN_STATE_SERVICE_ACCEPT;
return IPCOM_SUCCESS;
}
#if 0
/*
*===========================================================================
* ipssh_send_ignore_msg
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_ignore_msg(Ipssh_conn* ssh_conn, const char* msg)
{
Ipcom_buffer* pkt;
IPCOM_LOG0(DEBUG, "ipssh_send_ignore_msg()");
pkt = ipssh_buffer_new_v2_pkt(32 + ipcom_strlen(msg), IPSSH_MSG_IGNORE);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_ignore_msg :: out of memory");
return IPCOM_ERR_FAILED;
}
(void)ipssh_buffer_put_sshstr(pkt, msg);
if(ipssh_send_pkt(ssh_conn, pkt, IP_TRUE))
{
IPCOM_LOG0(ERR, "ipssh_send_ignore_msg :: ipssh_send_pkt() failed");
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
#endif
/*
*===========================================================================
* ipssh_send_new_key_msg
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_new_key_msg(Ipssh_conn* ssh_conn)
{
Ipcom_buffer* pkt;
const EVP_CIPHER* evp_cipher;
IPCOM_LOG0(DEBUG, "ipssh_send_new_key_msg()");
pkt = ipssh_buffer_new_v2_pkt(32, IPSSH_MSG_NEWKEYS);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_new_key_msg :: out of memory");
return IPCOM_ERR_FAILED;
}
if(ipssh_send_pkt(ssh_conn, pkt, IP_TRUE))
{
IPCOM_LOG0(ERR, "ipssh_send_new_key_msg :: ipssh_send_pkt() failed");
return IPCOM_ERR_FAILED;
}
/* All messages sent after NEWKEYS must use the negotiated ciphers and MACs.
* Setup cipher context */
switch(ssh_conn->mode)
{
case IPSSH_MODE_CLT:
ssh_conn->conn_prop.enc_alg_clt2srv[0] = ssh_conn->conn_prop_pending.enc_alg_clt2srv[0];
ssh_conn->conn_prop.mac_alg_clt2srv[0] = ssh_conn->conn_prop_pending.mac_alg_clt2srv[0];
ssh_conn->conn_prop.compr_alg_clt2srv[0] = ssh_conn->conn_prop_pending.compr_alg_clt2srv[0];
ssh_conn->conn_prop.lang_clt2srv[0] = ssh_conn->conn_prop_pending.lang_clt2srv[0];
ipssh_copy_key(&ssh_conn->key_mtrl.mac_key_c2s_active, &ssh_conn->key_mtrl.mac_key_c2s);
evp_cipher = ipssh_get_evp_enc(ssh_conn->conn_prop.enc_alg_clt2srv[0]);
if(!evp_cipher)
{
IPCOM_LOG1(ERR, "ipssh_send_new_key_msg :: EVP_get_cipherbyname() failed for %d",
ssh_conn->conn_prop.enc_alg_clt2srv[0]);
return IPCOM_ERR_FAILED;
}
(void)EVP_CIPHER_CTX_cleanup(&(ssh_conn->send_ctx));
if(!EVP_EncryptInit(&(ssh_conn->send_ctx),
evp_cipher,
ssh_conn->key_mtrl.key_c2s.key,
ssh_conn->key_mtrl.iv_c2s.key))
{
IPCOM_LOG0(ERR, "ipssh_send_new_key_msg :: EVP_EncryptInit() failed for clt2srv");
return IPCOM_ERR_FAILED;
}
(void)EVP_CIPHER_CTX_set_padding(&ssh_conn->send_ctx, 0);
if (ssh_conn->conn_prop.enc_alg_clt2srv[0] == IPSSH_ENC_ARCFOUR_128_ID)
ipssh_cipher_ctx_skip_bytes(&ssh_conn->send_ctx, 1536);
/* Mac */
if(ipssh_get_evp_md(ssh_conn->conn_prop.mac_alg_clt2srv[0], &ssh_conn->send_mac) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_send_new_key_msg :: ipssh_get_evp_md() failed");
return IPCOM_ERR_FAILED;
}
break;
case IPSSH_MODE_SRV:
ssh_conn->conn_prop.enc_alg_srv2clt[0] = ssh_conn->conn_prop_pending.enc_alg_srv2clt[0];
ssh_conn->conn_prop.mac_alg_srv2clt[0] = ssh_conn->conn_prop_pending.mac_alg_srv2clt[0];
ssh_conn->conn_prop.compr_alg_srv2clt[0] = ssh_conn->conn_prop_pending.compr_alg_srv2clt[0];
ssh_conn->conn_prop.lang_srv2clt[0] = ssh_conn->conn_prop_pending.lang_srv2clt[0];
ipssh_copy_key(&ssh_conn->key_mtrl.mac_key_s2c_active, &ssh_conn->key_mtrl.mac_key_s2c);
evp_cipher = ipssh_get_evp_enc(ssh_conn->conn_prop.enc_alg_srv2clt[0]);
if(!evp_cipher)
{
IPCOM_LOG1(ERR, "ipssh_send_new_key_msg :: EVP_get_cipherbyname() failed for %d",
ssh_conn->conn_prop.enc_alg_srv2clt[0]);
return IPCOM_ERR_FAILED;
}
(void)EVP_CIPHER_CTX_cleanup(&(ssh_conn->send_ctx));
if(!EVP_EncryptInit(&(ssh_conn->send_ctx),
evp_cipher,
ssh_conn->key_mtrl.key_s2c.key,
ssh_conn->key_mtrl.iv_s2c.key))
{
IPCOM_LOG0(ERR, "ipssh_send_new_key_msg :: EVP_EncryptInit() failed for clt2srv");
return IPCOM_ERR_FAILED;
}
(void)EVP_CIPHER_CTX_set_padding(&ssh_conn->send_ctx, 0);
if (ssh_conn->conn_prop.enc_alg_srv2clt[0] == IPSSH_ENC_ARCFOUR_128_ID)
ipssh_cipher_ctx_skip_bytes(&ssh_conn->send_ctx, 1536);
/* Mac */
if(ipssh_get_evp_md(ssh_conn->conn_prop.mac_alg_srv2clt[0], &ssh_conn->send_mac) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_send_new_key_msg :: ipssh_get_evp_md() failed");
return IPCOM_ERR_FAILED;
}
ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_NEW_KEY;
break;
default:
IP_PANIC();
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_consume_pkt
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_consume_pkt(Ipcom_buffer* buf)
{
Ip_u8* pkt;
int len;
pkt = ipcom_buffer_start_ptr(buf);
len = IPSSH_GET_PKT_LEN(pkt) - IPSSH_GET_PKT_PAD_LEN(pkt) + 4;
ip_assert((int)ipcom_buffer_len(buf) >= len); /*lint !e574 */
if(ipcom_buffer_consume(buf, len) != IPCOM_SUCCESS)
{
ipcom_buffer_reset(buf);
return IPCOM_ERR_FAILED;
}
if(ipcom_buffer_len(buf) == 0)
ipcom_buffer_reset(buf);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_msg_new_key
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_new_key(Ipssh_conn* ssh_conn)
{
const EVP_CIPHER* evp_cipher;
IPCOM_LOG0(DEBUG, "ipssh_msg_new_key()");
/* All messages sent after NEWKEYS must use the negotiated ciphers and MACs.
* Setup cipher context */
switch(ssh_conn->mode)
{
case IPSSH_MODE_CLT:
/* Activate the negotiated cipher, mac, etc. srv 2 clt */
ssh_conn->conn_prop.enc_alg_srv2clt[0] = ssh_conn->conn_prop_pending.enc_alg_srv2clt[0];
ssh_conn->conn_prop.mac_alg_srv2clt[0] = ssh_conn->conn_prop_pending.mac_alg_srv2clt[0];
ssh_conn->conn_prop.compr_alg_srv2clt[0] = ssh_conn->conn_prop_pending.compr_alg_srv2clt[0];
ssh_conn->conn_prop.lang_srv2clt[0] = ssh_conn->conn_prop_pending.lang_srv2clt[0];
ipssh_copy_key(&ssh_conn->key_mtrl.mac_key_s2c_active, &ssh_conn->key_mtrl.mac_key_s2c);
/* Enc, clt to srv */
evp_cipher = ipssh_get_evp_enc(ssh_conn->conn_prop.enc_alg_srv2clt[0]);
if(!evp_cipher)
{
IPCOM_LOG1(ERR, "ipssh_msg_new_key :: EVP_get_cipherbyname() failed for %d",
ssh_conn->conn_prop.enc_alg_srv2clt[0]);
return IPCOM_ERR_FAILED;
}
(void)EVP_CIPHER_CTX_cleanup(&(ssh_conn->recv_ctx));
if(!EVP_DecryptInit(&(ssh_conn->recv_ctx),
evp_cipher,
ssh_conn->key_mtrl.key_s2c.key,
ssh_conn->key_mtrl.iv_s2c.key))
{
IPCOM_LOG0(ERR, "ipssh_msg_new_key :: EVP_DecryptInit() failed for srv2clt");
return IPCOM_ERR_FAILED;
}
(void)EVP_CIPHER_CTX_set_padding(&ssh_conn->recv_ctx, 0);
if (ssh_conn->conn_prop.enc_alg_clt2srv[0] == IPSSH_ENC_ARCFOUR_128_ID)
ipssh_cipher_ctx_skip_bytes(&ssh_conn->recv_ctx, 1536);
/* Mac */
if(ipssh_get_evp_md(ssh_conn->conn_prop.mac_alg_srv2clt[0], &ssh_conn->recv_mac) != IPCOM_SUCCESS)
{
IPCOM_LOG1(ERR, "ipssh_msg_new_key :: ipssh_get_evp_md() failed, alg id: %d", ssh_conn->conn_prop.mac_alg_srv2clt[0]);
return IPCOM_ERR_FAILED;
}
break;
case IPSSH_MODE_SRV:
/* Activate the negotiated cipher, mac, etc. clt 2 srv */
ssh_conn->conn_prop.enc_alg_clt2srv[0] = ssh_conn->conn_prop_pending.enc_alg_clt2srv[0];
ssh_conn->conn_prop.mac_alg_clt2srv[0] = ssh_conn->conn_prop_pending.mac_alg_clt2srv[0];
ssh_conn->conn_prop.compr_alg_clt2srv[0] = ssh_conn->conn_prop_pending.compr_alg_clt2srv[0];
ssh_conn->conn_prop.lang_clt2srv[0] = ssh_conn->conn_prop_pending.lang_clt2srv[0];
ipssh_copy_key(&ssh_conn->key_mtrl.mac_key_c2s_active, &ssh_conn->key_mtrl.mac_key_c2s);
/* Enc, clt to srv */
evp_cipher = ipssh_get_evp_enc(ssh_conn->conn_prop.enc_alg_clt2srv[0]);
if(!evp_cipher)
{
IPCOM_LOG1(ERR, "ipssh_msg_new_key :: EVP_get_cipherbyname() failed for %d",
ssh_conn->conn_prop.enc_alg_clt2srv[0]);
return IPCOM_ERR_FAILED;
}
(void)EVP_CIPHER_CTX_cleanup(&(ssh_conn->recv_ctx));
if(!EVP_DecryptInit(&(ssh_conn->recv_ctx),
evp_cipher,
ssh_conn->key_mtrl.key_c2s.key,
ssh_conn->key_mtrl.iv_c2s.key))
{
IPCOM_LOG0(ERR, "ipssh_msg_new_key :: EVP_DecryptInit() failed for clt2srv");
return IPCOM_ERR_FAILED;
}
(void)EVP_CIPHER_CTX_set_padding(&ssh_conn->recv_ctx, 0);
if (ssh_conn->conn_prop.enc_alg_srv2clt[0] == IPSSH_ENC_ARCFOUR_128_ID)
ipssh_cipher_ctx_skip_bytes(&ssh_conn->recv_ctx, 1536);
/* Mac */
if(ipssh_get_evp_md(ssh_conn->conn_prop.mac_alg_clt2srv[0], &ssh_conn->recv_mac) != IPCOM_SUCCESS)
{
IPCOM_LOG1(ERR, "ipssh_msg_new_key :: ipssh_get_evp_md() failed, alg id: %d", ssh_conn->conn_prop.mac_alg_clt2srv[0]);
return IPCOM_ERR_FAILED;
}
break;
default:
IP_PANIC();
}
if (ssh_conn->post_kex_conn_state == 0)
{
/* This is an inital key exchange that has completed */
ssh_conn->conn_state = IPSSH_CONN_STATE_SERVICE_REQ;
}
else
{
/* This is a re-keying exchange that has completed */
ssh_conn->conn_state = ssh_conn->post_kex_conn_state;
}
ipssh_free_handshake_msgs(ssh_conn, IPSSH_HANDSHAKE_MSG_FLAG_SAVE_VER_STR);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_msg_service_accept
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_service_accept(Ipssh_conn* ssh_conn)
{
char* service;
Ip_s32 rc = IPCOM_ERR_FAILED;
IPCOM_LOG0(DEBUG2, "ipssh_msg_service_accept()");
(void)ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6);
service = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf);
if(!service)
{
IPCOM_LOG0(ERR, "ipssh_msg_service_accept :: ipssh_buffer_get_sshstr() failed");
goto cleanup;
}
IPCOM_LOG1(DEBUG, "ipssh_msg_service_accept :: service name: %s", service);
if(ipcom_strcmp(service, IPSSH_USERAUTH_STR) != 0)
{
IPCOM_LOG1(ERR, "ipssh_msg_service_accept :: unexpected service name: %s", service);
goto cleanup;
}
ssh_conn->conn_state = IPSSH_CONN_STATE_CLT_USER_AUTH_START;
rc = IPCOM_SUCCESS;
cleanup:
if(service)
ipcom_free(service);
return rc;
}
/*
*===========================================================================
* ipssh_msg_service_req
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_service_req(Ipssh_conn* ssh_conn)
{
char* service_name;
Ip_s32 rc = IPCOM_SUCCESS;
IPCOM_LOG0(DEBUG2, "ipssh_msg_service_req()");
(void)ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6);
service_name = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf);
if(!service_name)
{
IPCOM_LOG0(ERR, "ipssh_msg_service_req :: ipssh_buffer_get_sshstr() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
IPCOM_LOG1(DEBUG, "ipssh_msg_service_req :: service name: %s", service_name);
if(ipcom_strcmp(service_name, IPSSH_USERAUTH_STR) == 0)
{
ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_USER_AUTH;
rc = ipssh_send_service_accept(ssh_conn, service_name);
goto cleanup;
}
/* Unknown service */
IPCOM_LOG1(ERR, "ipssh_msg_service_req :: unknown service: %s", service_name);
rc = IPCOM_ERR_FAILED;
cleanup:
if(service_name)
ipcom_free(service_name);
return rc;
}
/*
*===========================================================================
* ipssh_compute_keys
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_compute_keys(Ipssh_conn* ssh_conn)
{
IPCOM_LOG0(DEBUG2, "ipssh_compute_keys()");
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_s2c.key)
ipcom_free(ssh_conn->key_mtrl.mac_key_s2c.key);
if(ipssh_compute_key(ssh_conn, 'A', &(ssh_conn->key_mtrl.iv_c2s)) ||
ipssh_compute_key(ssh_conn, 'B', &(ssh_conn->key_mtrl.iv_s2c)) ||
ipssh_compute_key(ssh_conn, 'C', &(ssh_conn->key_mtrl.key_c2s)) ||
ipssh_compute_key(ssh_conn, 'D', &(ssh_conn->key_mtrl.key_s2c)) ||
ipssh_compute_key(ssh_conn, 'E', &(ssh_conn->key_mtrl.mac_key_c2s)) ||
ipssh_compute_key(ssh_conn, 'F', &(ssh_conn->key_mtrl.mac_key_s2c)))
{
IPCOM_LOG0(ERR, "ipssh_compute_keys :: ipssh_compute_key() failed");
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_send_kexinit
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_kexinit(Ipssh_conn* ssh_conn)
{
Ip_s32 i;
Ip_err rc;
Ipcom_buffer* pkt;
Ip_size_t hash_len;
Ip_u8* hash_data;
Ip_u8 hash_pos;
IPCOM_LOG0(DEBUG, "ipssh_send_kexinit()");
ip_assert(ssh_conn->mode == IPSSH_MODE_CLT || ssh_conn->mode == IPSSH_MODE_SRV);
pkt = ipssh_buffer_new_v2_pkt(2048, IPSSH_MSG_KEXINIT);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_kexinit :: out of memory");
return IPCOM_ERR_FAILED;
}
/* Append cookie, 16 bytes of random data */
for(i=0; i<4; i++)
{
Ip_u32 rnd = ipcom_random();
(void)ipcom_buffer_put(pkt, (Ip_u8*) &rnd, sizeof(rnd));
}
if(ipssh_append_list(pkt, ssh_conn->config.v2_algs.kex_alg) ||
ipssh_append_list(pkt, ssh_conn->config.v2_algs.srv_host_key_alg) ||
ipssh_append_list(pkt, ssh_conn->config.v2_algs.enc_alg_clt2srv) ||
ipssh_append_list(pkt, ssh_conn->config.v2_algs.enc_alg_srv2clt) ||
ipssh_append_list(pkt, ssh_conn->config.v2_algs.mac_alg_clt2srv) ||
ipssh_append_list(pkt, ssh_conn->config.v2_algs.mac_alg_srv2clt) ||
ipssh_append_list(pkt, ssh_conn->config.v2_algs.compr_alg_clt2srv) ||
ipssh_append_list(pkt, ssh_conn->config.v2_algs.compr_alg_srv2clt) ||
ipssh_append_list(pkt, ssh_conn->config.v2_algs.lang_clt2srv) ||
ipssh_append_list(pkt, ssh_conn->config.v2_algs.lang_srv2clt) ||
ipcom_buffer_put_char(pkt, 0) /* No first kex packet follows */ ||
ipcom_buffer_put_int(pkt, 0)/* Add reserved field */)
{
IPCOM_LOG0(ERR, "ipssh_send_kexinit :: ipssh_append_list() failed.");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
/* Store packet, needed when kex exchange hash shall be calculated */
hash_data = IPSSH_GET_PKT_PAYLOAD(ipcom_buffer_start_ptr(pkt));
hash_len = ipcom_buffer_len(pkt)-5;
switch(ssh_conn->mode)
{
case IPSSH_MODE_CLT:
hash_pos = 2;
break;
case IPSSH_MODE_SRV:
hash_pos = 3;
break;
default:
IP_PANIC();
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), IPSSH_STR, hash_data, hash_len, hash_pos))
{
IPCOM_LOG0(ERR, "ipssh_send_kexinit :: ipssh_msg2list() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
/* Send packet */
rc = ipssh_send_pkt(ssh_conn, pkt, IP_FALSE);
if (ssh_conn->kexinit_received)
ssh_conn->kexinit_received = IP_FALSE;
else
ssh_conn->kexinit_sent = IP_TRUE;
cleanup:
if(pkt)
ipcom_buffer_free(pkt);
return rc;
}
/*
*===========================================================================
* ipssh_send_pkt
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_pkt(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt, Ip_bool free_pkt)
{
Ip_err rc = IPCOM_ERR_FAILED;
Ip_ssize_t bytes_sent = 0;
Ip_u8 pad_len;
Ip_u32 msg_len;
Ip_s32 enc_msg_len, len;
Ip_u32 seq_no_nw_order, mac_len=0;
Ip_u8* len_ptr;
Ipcom_buffer* enc_pkt = IP_NULL;
Ip_u8 block_size;
/* Update padding length and add padding */
block_size = (Ip_u8)IP_MAX(ssh_conn->send_ctx.cipher->block_size, 8);
pad_len = (Ip_u8) (block_size - (ipcom_buffer_len(pkt) % block_size));
if(pad_len < 4) /* At least 4 bytes of padding, according to spec */
pad_len = (Ip_u8)(pad_len + block_size);
IPSSH_SET_PKT_PAD_LEN(ipcom_buffer_start_ptr(pkt), pad_len);
if(ipcom_buffer_decr_cap(pkt, pad_len))
{
IPCOM_LOG0(ERR, "ipssh_send_pkt :: no space for padding");
goto cleanup;
}
/* Update length field, the length field is not included in the length count */
msg_len = (Ip_s32)ipcom_buffer_len(pkt)-4;
len_ptr = ipcom_buffer_start_ptr(pkt);
IP_SET_32ON8(len_ptr, ip_htonl(msg_len));
#ifdef IPSSH_DEBUG
IPCOM_LOG4(DEBUG2, "ipssh_send_pkt :: type: %s (%d), len=%d, pad len=%d",
ipssh_get_msg_name(IPSSH_GET_PKT_TYPE(ipcom_buffer_start_ptr(pkt))),
IPSSH_GET_PKT_TYPE(ipcom_buffer_start_ptr(pkt)), msg_len, pad_len);
#endif
if(ssh_conn->send_mac.md)
enc_msg_len = msg_len + 4 + ssh_conn->send_mac.md->md_size;
else
enc_msg_len = msg_len + 4;
enc_pkt = ipcom_buffer_new(enc_msg_len);
if(!enc_pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_pkt :: out of memory");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
/* Encrypt packet */
if(EVP_EncryptUpdate(&(ssh_conn->send_ctx), ipcom_buffer_end_ptr(enc_pkt), (int*)&len,
ipcom_buffer_start_ptr(pkt), (int)ipcom_buffer_len(pkt)) != 1)
{
IPCOM_LOG0(ERR, "ipssh_send_pkt :: EVP_EncryptUpdate() failed");
goto cleanup;
}
(void)ipcom_buffer_decr_cap(enc_pkt, len);
/* Add mac = MAC (mac_key | seq_no | unencrypted packet) */
if(ssh_conn->send_mac.md)
{
Ipssh_key* mac_key;
switch(ssh_conn->mode)
{
case IPSSH_MODE_CLT:
mac_key = &ssh_conn->key_mtrl.mac_key_c2s_active;
break;
case IPSSH_MODE_SRV:
mac_key = &ssh_conn->key_mtrl.mac_key_s2c_active;
break;
default:
IP_PANIC();
goto cleanup;
}
seq_no_nw_order = ip_htonl(ssh_conn->seq_no_send);
HMAC_CTX_init(&(ssh_conn->send_hmac_ctx));
HMAC_Init(&(ssh_conn->send_hmac_ctx),
mac_key->key,
mac_key->len,
ssh_conn->send_mac.md);
HMAC_Update(&(ssh_conn->send_hmac_ctx), (Ip_u8*)&(seq_no_nw_order), 4);
HMAC_Update(&(ssh_conn->send_hmac_ctx), ipcom_buffer_start_ptr(pkt), ipcom_buffer_len(pkt));
HMAC_Final(&(ssh_conn->send_hmac_ctx), ipcom_buffer_end_ptr(enc_pkt), (unsigned int*)&mac_len);
HMAC_CTX_cleanup(&(ssh_conn->send_hmac_ctx));
if(ssh_conn->send_mac.use_bits != 0)
{
ip_assert(ssh_conn->send_mac.use_bits % 8 == 0);
(void)ipcom_buffer_decr_cap(enc_pkt, ssh_conn->send_mac.use_bits/8);
}
else
(void)ipcom_buffer_decr_cap(enc_pkt, mac_len);
}
#if defined(IPSSH_DEBUG) && defined (IPSSH_PKT_DBG)
IPCOM_LOG0(DEBUG, "clear text packet:");
ipcom_buffer_dump(pkt);
IPCOM_LOG0(DEBUG, "encrypted packet:");
ipcom_buffer_dump(enc_pkt);
#endif
while(ipcom_buffer_len(enc_pkt) > 0)
{
bytes_sent = ipcom_send(ssh_conn->sock,
ipcom_buffer_start_ptr(enc_pkt),
ipcom_buffer_len(enc_pkt),
0);
if(bytes_sent < 0)
{
if(ipcom_errno == IP_ERRNO_EWOULDBLOCK)
{
ipcom_millisleep(1);
continue;
}
IPCOM_LOG1(ERR, "ipssh_send_pkt :: ipcom_send() failed, errno=%d", ipcom_errno);
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
(void)ipcom_buffer_consume(enc_pkt, bytes_sent);
}
(void)ipcom_buffer_consume(pkt, msg_len + 4); /* Msg len does not include the 4 byte length field */
if(ipcom_buffer_len(pkt) == 0)
ipcom_buffer_reset(pkt);
ssh_conn->seq_no_send++;
rc = IPCOM_SUCCESS;
cleanup:
if(enc_pkt)
ipcom_buffer_free(enc_pkt);
if(free_pkt)
ipcom_buffer_free(pkt);
return rc;
}
/*
*===========================================================================
* ipssh_decrypt_pkt
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_decrypt_pkt(Ipssh_conn* ssh_conn)
{
int pkt_len;
int remaining_bytes;
int len;
Ip_u8* pkt;
if(ipcom_buffer_capacity(ssh_conn->recv_enc_buf) != 0)
{
return IPSSH_NOT_COMPL_PKT;
}
pkt = ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf);
pkt_len = IPSSH_GET_PKT_LEN(pkt);
/* RFC4253 section 6.
* The minimum size of a packet is 16 (or the cipher block size,
* whichever is larger) bytes (plus 'mac')
*/
if (pkt_len+4 < 16)
{
IPCOM_LOG1(DEBUG2,"ipssh_decrypt_pkt :: bad packet length, pkt len=%d",pkt_len);
return IPCOM_ERR_FAILED;
}
remaining_bytes = pkt_len - (int)ipcom_buffer_len(ssh_conn->recv_plain_buf) + 4;
if(remaining_bytes > (int)ipcom_buffer_len(ssh_conn->recv_enc_buf))
{
IPCOM_LOG2(DEBUG2,"ipssh_decrypt_pkt :: bad packet length, pkt len=%d, buf len=%d",
pkt_len, ipcom_buffer_len(ssh_conn->recv_enc_buf));
return IPCOM_ERR_FAILED;
}
if(EVP_DecryptUpdate(&(ssh_conn->recv_ctx), ipcom_buffer_end_ptr(ssh_conn->recv_plain_buf), (int*)&len,
ipcom_buffer_start_ptr(ssh_conn->recv_enc_buf), remaining_bytes) != 1)
{
IPCOM_LOG0(ERR,"ipssh_decrypt_pkt :: EVP_DecryptUpdate() failed");
return IPCOM_ERR_FAILED;
}
ip_assert(len == remaining_bytes);
(void)ipcom_buffer_consume(ssh_conn->recv_enc_buf, remaining_bytes);
(void)ipcom_buffer_decr_cap(ssh_conn->recv_plain_buf, remaining_bytes);
if(ssh_conn->recv_mac.md && ssh_conn->recv_mac.md->md_size != 0)
{
/* Handle MAC */
Ip_u8* mac;
Ip_u32 mac_len;
Ip_u32 seq_no_nw_order;
Ipssh_key* mac_key;
seq_no_nw_order = ip_htonl(ssh_conn->seq_no_recv);
switch(ssh_conn->mode)
{
case IPSSH_MODE_CLT:
mac_key = &ssh_conn->key_mtrl.mac_key_s2c_active;
break;
case IPSSH_MODE_SRV:
mac_key = &ssh_conn->key_mtrl.mac_key_c2s_active;
break;
default:
IP_PANIC();
return IPCOM_ERR_FAILED;
}
mac = ipcom_malloc(ssh_conn->recv_mac.md->md_size);
if(!mac)
return IPCOM_ERR_FAILED;
HMAC_CTX_init(&(ssh_conn->recv_hmac_ctx));
HMAC_Init(&(ssh_conn->recv_hmac_ctx),
mac_key->key,
mac_key->len,
ssh_conn->recv_mac.md);
HMAC_Update(&(ssh_conn->recv_hmac_ctx), (Ip_u8*)&(seq_no_nw_order), 4);
HMAC_Update(&(ssh_conn->recv_hmac_ctx), pkt, pkt_len+4);
HMAC_Final(&(ssh_conn->recv_hmac_ctx), mac, (unsigned int*)&mac_len);
HMAC_CTX_cleanup(&(ssh_conn->recv_hmac_ctx));
if(ssh_conn->recv_mac.use_bits != 0)
{
ip_assert(ssh_conn->recv_mac.use_bits/8 <= (int)mac_len);
mac_len = ssh_conn->recv_mac.use_bits/8;
}
if(ipcom_memcmp(mac,ipcom_buffer_start_ptr(ssh_conn->recv_enc_buf), mac_len))
{
IPCOM_LOG1(ERR, "ipssh_decrypt_pkt :: MAC check failed: seq no=%d", ssh_conn->seq_no_recv);
ipcom_free(mac);
return IPSSH_BAD_MAC;
}
ipcom_free(mac);
(void)ipcom_buffer_consume(ssh_conn->recv_enc_buf, mac_len);
}
ssh_conn->seq_no_recv++;
/* Remove padding */
if(ipcom_buffer_consume_end(ssh_conn->recv_plain_buf, IPSSH_GET_PKT_PAD_LEN(pkt)) != IPCOM_SUCCESS)
return IPCOM_ERR_FAILED;
if(ipcom_buffer_len(ssh_conn->recv_enc_buf) == 0)
ipcom_buffer_reset(ssh_conn->recv_enc_buf);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_read_and_dispatch_v2_pkt
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_read_and_dispatch_v2_pkt(Ipssh_conn* ssh_conn, Ip_u8 expected_pkt_type)
{
Ip_err rc;
while (1)
{
rc =ipssh_read_v2_pkt(ssh_conn);
if(rc != IPCOM_SUCCESS)
return rc;
if (expected_pkt_type == 0 || expected_pkt_type == IPSSH_GET_PKT_TYPE(ssh_conn->recv_plain_buf->buf))
return ipssh_pkt_dispatch(ssh_conn);
IPCOM_LOG2(DEBUG, "ipssh_read_and_dispatch_v2_pkt :: expected pkt type %d, got %d",
expected_pkt_type, IPSSH_GET_PKT_TYPE(ssh_conn->recv_plain_buf->buf));
rc = ipssh_pkt_dispatch(ssh_conn);
if (rc != IPCOM_SUCCESS)
return rc;
}
}
/*
*===========================================================================
* ipssh_read_v2_pkt2
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL int
ipssh_read_v2_pkt2(Ipssh_conn* ssh_conn, Ip_u8 expected_pkt_type)
{
Ip_err rc;
while (1)
{
rc = ipssh_read_v2_pkt(ssh_conn);
if(rc != IPCOM_SUCCESS)
return rc;
if (expected_pkt_type != IPSSH_GET_PKT_TYPE(ssh_conn->recv_plain_buf->buf))
{
IPCOM_LOG2(DEBUG, "ipssh_read_v2_pkt2 :: expected pkt type %d, got %d",
expected_pkt_type, IPSSH_GET_PKT_TYPE(ssh_conn->recv_plain_buf->buf));
rc = ipssh_pkt_dispatch(ssh_conn);
if (rc != IPCOM_SUCCESS)
return rc;
continue;
}
break;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_read_v2_pkt
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL int
ipssh_read_v2_pkt(Ipssh_conn* ssh_conn)
{
Ip_ssize_t rc;
IPCOM_LOG0(DEBUG2, "ipssh_read_v2_pkt :: ");
if(!ssh_conn->recv_plain_buf || ssh_conn->complete_pkt_read)
{
switch( (rc = ipssh_setup_recv_bufs(ssh_conn)))
{
case IPCOM_SUCCESS:
break;
case IPCOM_ERR_FAILED:
IPCOM_LOG0(ERR, "ipssh_read_v2_pkt :: ipssh_setup_recv_bufs() failed");
return IPCOM_ERR_FAILED;
case IPSSH_CONN_CLOSED:
return IPCOM_ERR_FAILED;
default:
IPCOM_LOG1(ERR, "ipssh_read_v2_pkt :: ipssh_setup_recv_bufs() returned unknown value: %d", rc);
IP_PANIC();
}
}
while(1)
{
ip_assert(ipcom_buffer_capacity(ssh_conn->recv_enc_buf));
rc = ipcom_socketread(ssh_conn->sock, ipcom_buffer_end_ptr(ssh_conn->recv_enc_buf),
ipcom_buffer_capacity(ssh_conn->recv_enc_buf));
IPCOM_LOG1(DEBUG2, "ipssh_read_v2_pkt :: read %d bytes", rc);
if(rc < 0)
{
if(ipcom_errno == IP_ERRNO_EWOULDBLOCK)
{
IPCOM_LOG0(DEBUG2, "ipssh_read_v2_pkt :: all available data read");
return IPCOM_SUCCESS;
}
IPCOM_LOG1(ERR, "ipssh_read_v2_pkt :: ipcom_socketread() failed, errno=%d",
ipcom_errno);
return IPCOM_ERR_FAILED;
}
(void)ipcom_buffer_decr_cap(ssh_conn->recv_enc_buf, rc);
if(ipcom_buffer_capacity(ssh_conn->recv_enc_buf) == 0)
{
ssh_conn->complete_pkt_read = IP_TRUE;
IPCOM_LOG0(DEBUG2, "ipssh_read_v2_pkt :: complete packet read");
break;
}
if(rc == 0)
{
IPCOM_LOG0(INFO, "ipssh_read_v2_pkt :: connection closed by client");
return IPSSH_CONN_CLOSED;
}
}
switch(ipssh_decrypt_pkt(ssh_conn))
{
case IPCOM_SUCCESS:
break;
case IPSSH_NOT_COMPL_PKT:
IPCOM_LOG1(DEBUG2, "ipssh_read_v2_pkt :: Entire packet not received. buf len=%d",
ipcom_buffer_len(ssh_conn->recv_plain_buf));
return IPCOM_SUCCESS;
case IPSSH_ERR:
case IPSSH_BAD_MAC:
case IPCOM_ERR_FAILED:
return IPCOM_ERR_FAILED;
default:
IP_PANIC();
break;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_conn_v2_handler
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_conn_v2_handler(Ip_fd sock, Ipssh_conn_ctx* ctx, Ipssh_map_entry* map_entry)
{
Ip_ssize_t rc;
(void)map_entry;
IPCOM_LOG1(DEBUG2, "ipssh_conn_v2_handler(), sock=%d", sock);
if(!ctx->ssh_conn->recv_plain_buf)
{
switch( (rc = ipssh_setup_recv_bufs(ctx->ssh_conn)))
{
case IPCOM_SUCCESS:
break;
case IPCOM_ERR_FAILED:
IPCOM_LOG0(ERR, "ipssh_conn_v2_handler :: ipssh_setup_recv_bufs() failed");
return IPCOM_ERR_FAILED;
case IPSSH_CONN_CLOSED:
return IPCOM_ERR_FAILED;
default:
IPCOM_LOG1(ERR, "ipssh_conn_v2_handler :: ipssh_setup_recv_bufs() returned unknown value: %d", rc);
IP_PANIC();
}
}
while(1)
{
ip_assert(ipcom_buffer_capacity(ctx->ssh_conn->recv_enc_buf));
rc = ipcom_socketread(sock, ipcom_buffer_end_ptr(ctx->ssh_conn->recv_enc_buf),
ipcom_buffer_capacity(ctx->ssh_conn->recv_enc_buf));
IPCOM_LOG1(DEBUG2, "ipssh_conn_v2_handler :: read %d bytes", rc);
if(rc < 0)
{
if(ipcom_errno == IP_ERRNO_EWOULDBLOCK)
{
IPCOM_LOG0(DEBUG2, "ipssh_conn_v2_handler :: all available data read");
break;
}
IPCOM_LOG1(ERR, "ipssh_conn_v2_handler :: ipcom_socketread() failed, errno=%d",
ipcom_errno);
return IPCOM_ERR_FAILED;
}
(void)ipcom_buffer_decr_cap(ctx->ssh_conn->recv_enc_buf, rc);
if(ipcom_buffer_capacity(ctx->ssh_conn->recv_enc_buf) == 0)
{
IPCOM_LOG0(DEBUG2, "ipssh_conn_v2_handler :: buffer full");
break;
}
if(rc == 0)
{
IPCOM_LOG0(INFO, "ipssh_conn_v2_handler :: connection closed by client");
return IPCOM_ERR_FAILED;
}
}
switch(ipssh_decrypt_pkt(ctx->ssh_conn))
{
case IPCOM_SUCCESS:
if(ipssh_pkt_dispatch(ctx->ssh_conn))
{
IPCOM_LOG0(ERR, "ipssh_conn_v2_handler :: ipssh_pkt_dispatcher() failed");
return IPCOM_ERR_FAILED;
}
/*ip_assert(ipcom_buffer_len(ctx->ssh_conn->recv_plain_buf) == 0);*/
ipssh_reset_buffers(ctx->ssh_conn);
break;
case IPSSH_NOT_COMPL_PKT:
IPCOM_LOG1(DEBUG2, "ipssh_conn_v2_handler :: Entire packet not received. buf len=%d",
ipcom_buffer_len(ctx->ssh_conn->recv_plain_buf));
return IPCOM_SUCCESS;
case IPCOM_ERR_FAILED:
case IPSSH_ERR:
case IPSSH_BAD_MAC:
return IPCOM_ERR_FAILED;
default:
IP_PANIC();
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_msg_kexinit
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_kexinit(Ipssh_conn* ssh_conn)
{
Ip_u8* pkt;
Ip_u8* hash_data;
Ip_u32 hash_len,pkt_len;
Ip_u8 hash_pos,pad_len;
IPCOM_LOG0(DEBUG,"ipssh_msg_kexinit()");
if (ssh_conn->conn_state != IPSSH_CONN_STATE_SRV_KEX_INIT)
{
/* This is a re-keying request, save current state */
ssh_conn->post_kex_conn_state = ssh_conn->conn_state;
ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_KEX_INIT;
}
/* Store packet, needed when kex exchange hash shall be calculated */
pkt = ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf);
pkt_len = IPSSH_GET_PKT_LEN(pkt);
pad_len = IPSSH_GET_PKT_PAD_LEN(pkt);
hash_data = IPSSH_GET_PKT_PAYLOAD(pkt);
hash_len = pkt_len - pad_len - 1;
switch(ssh_conn->mode)
{
case IPSSH_MODE_CLT:
hash_pos = 3;
break;
case IPSSH_MODE_SRV:
hash_pos = 2;
break;
default:
IP_PANIC();
return IPCOM_ERR_FAILED;
}
if(ipssh_msg2list((Ipssh_msg_list*)&(ssh_conn->handshake_msgs), IPSSH_STR, hash_data, hash_len, hash_pos))
{
IPCOM_LOG0(ERR, "ipssh_msg_kexinit :: ipssh_msg2list() failed");
return IPCOM_ERR_FAILED;
}
(void)ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6);
if(ipssh_parse_kexinit(ssh_conn, ssh_conn->recv_plain_buf))
{
IPCOM_LOG0(ERR, "ipssh_msg_kexinit :: ipssh_parse_kexinit() failed");
return IPCOM_ERR_FAILED;
}
if (!ssh_conn->kexinit_sent)
{
ssh_conn->kexinit_received = IP_TRUE;
return ipssh_send_kexinit(ssh_conn);
}
else
ssh_conn->kexinit_sent = IP_FALSE;
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_msg2list
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg2list(Ipssh_msg_list* list, Ip_u8 type, Ip_u8* msg, Ip_size_t len, Ip_u8 hash_pos)
{
Ipssh_msg_list* node;
Ip_u8* value;
node = ipcom_malloc(sizeof(Ipssh_msg_list));
value = ipcom_malloc(len);
if(!node || !value)
{
IPCOM_LOG0(ERR, "ipssh_msg2list :: out of memory");
if(node)
ipcom_free(node);
if(value)
ipcom_free(value);
return IPCOM_ERR_FAILED;
}
node->value = value;
ipcom_memcpy(node->value, msg, len);
node->msg_len = len;
node->hash_pos = hash_pos;
node->type = type;
ipcom_list_insert_last((Ipcom_list*)list, (Ipcom_list*)node);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_msg2list_int
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg2list_int(Ipssh_msg_list* list, Ip_u32 int_value, Ip_u8 hash_pos)
{
Ipssh_msg_list* node;
Ip_u8* value;
node = ipcom_malloc(sizeof(Ipssh_msg_list));
value = ipcom_malloc(sizeof(int_value));
if(!node || !value)
{
IPCOM_LOG0(ERR, "ipssh_msg2list :: out of memory");
if(node)
ipcom_free(node);
if(value)
ipcom_free(value);
return IPCOM_ERR_FAILED;
}
node->value = value;
IP_SET_32ON8(node->value, ip_htonl(int_value));
node->msg_len = sizeof(int_value);
node->hash_pos = hash_pos;
node->type = IPSSH_INT;
ipcom_list_insert_last((Ipcom_list*)list, (Ipcom_list*)node);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_reset_buffers
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL void
ipssh_reset_buffers(Ipssh_conn* ssh_conn)
{
if(ssh_conn->recv_plain_buf)
{
ipcom_buffer_free(ssh_conn->recv_plain_buf);
ssh_conn->recv_plain_buf = IP_NULL;
}
if(ssh_conn->recv_enc_buf)
{
ipcom_buffer_free(ssh_conn->recv_enc_buf);
ssh_conn->recv_enc_buf = IP_NULL;
}
}
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/