/* ****************************************************************************** * 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 ****************************************************************************** */ /* 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 #include #include #include #include #include #include #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 **************************************************************************** */