2716 lines
84 KiB
C
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
|
|
****************************************************************************
|
|
*/
|