/* ****************************************************************************** * SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipssh_buffer.c,v $ $Revision: 1.53.10.5 $ * $Source: /home/interpeak/CVSRoot/ipssh/src/ipssh_buffer.c,v $ * $Author: jren $ $Date: 2016-10-31 08:49:42 $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Roger Boden ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #include "ipssh_h.h" #include "ipssh_buffer.h" #include #include #include #include #include /* **************************************************************************** * 4 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 5 DEFINES **************************************************************************** */ /* **************************************************************************** * 6 TYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 LOCAL FUNCTIONS **************************************************************************** */ /* **************************************************************************** * 10 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipssh_buffer_get_sshstr *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC char* #if defined(IPCOM_USE_DMALLOC) && !defined(IPCOM_DMALLOC_C) ipssh_buffer_get_sshstr_d(Ipcom_buffer* buf, const char *file, const char *function, Ip_u32 line) #else ipssh_buffer_get_sshstr(Ipcom_buffer* buf) #endif { Ip_u32 len; char* str; if(ipssh_buffer_get_uint(buf, &len) != IPCOM_SUCCESS) return IP_NULL; if(len > ipcom_buffer_len(buf)) return IP_NULL; #if defined(IPCOM_USE_DMALLOC) && !defined(IPCOM_DMALLOC_C) str = ipcom_dmalloc(len+1, file, function, line); #else str = ipcom_malloc(len+1); #endif if(!str) return IP_NULL; if(ipcom_buffer_get(buf, (Ip_u8*)str, len) != IPCOM_SUCCESS) { ipcom_free(str); return IP_NULL; } str[len] = 0; return str; } /* *=========================================================================== * ipssh_buffer_get_sshbuf *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_u8* ipssh_buffer_get_sshbuf(Ipcom_buffer* buf, Ip_u32* len) { char* data; if(ipssh_buffer_get_uint(buf, len) != IPCOM_SUCCESS) return IP_NULL; if(*len == 0) return IP_NULL; if(*len>ipcom_buffer_len(buf)) return IP_NULL; data = ipcom_malloc(*len); if(!data) return IP_NULL; if(ipcom_buffer_get(buf, (Ip_u8*)data, *len) != IPCOM_SUCCESS) { ipcom_free(data); return IP_NULL; } return (Ip_u8*)data; } /* *=========================================================================== * ipssh_buffer_put_sshstr *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipssh_buffer_put_sshstr(Ipcom_buffer* buf, const char* str) { Ip_size_t len; if(!str) return ipcom_buffer_put_int(buf, 0); len = ipcom_strlen(str); if(ipcom_buffer_put_int(buf, ip_htonl(len))) return IPCOM_ERR_FAILED; return ipcom_buffer_put(buf, (Ip_u8*)str, len); } /* *=========================================================================== * ipssh_buffer_put_sshbuf *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipssh_buffer_put_sshbuf(Ipcom_buffer* buf, const Ip_u8* str, Ip_size_t len) { if ( ipcom_buffer_put_int(buf, ip_htonl(len))) return IPCOM_ERR_FAILED; return ipcom_buffer_put(buf, (Ip_u8*)str, len); } /* *=========================================================================== * ipssh_buffer_put_bn *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipssh_buffer_put_bn(Ipcom_buffer* buf, BIGNUM* bn) { int len; Ip_u8* tmp_bn; Ip_s32 rc; len = BN_num_bytes(bn); if (len == 0) return IPCOM_ERR_FAILED; if(!(tmp_bn = ipcom_malloc(len+1))) return IPCOM_ERR_FAILED; tmp_bn[0] = 0; if(BN_bn2bin(bn, tmp_bn+1) != len) { ipcom_free(tmp_bn); return IPCOM_ERR_FAILED; } if(tmp_bn[1] & 0x80) /* if most significant bit is set, a zero byte must be added */ { len++; rc = ipssh_buffer_put_ssh_mpint(buf, tmp_bn, len); } else rc = ipssh_buffer_put_ssh_mpint(buf, &(tmp_bn[1]), len); ipcom_free(tmp_bn); if(rc != IPCOM_SUCCESS) return IPCOM_ERR_FAILED; else return len; } /* *=========================================================================== * ipssh_buffer_get_ssh_mpint2 *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipssh_buffer_get_ssh_mpint2(Ipcom_buffer* buf, BIGNUM** bnp) { Ip_u32 len; BIGNUM* bn = *bnp; if(ipssh_buffer_get_uint(buf, &len) != IPCOM_SUCCESS) return IPCOM_ERR_FAILED; if(len>ipcom_buffer_len(buf)) return IPCOM_ERR_FAILED; bn = BN_bin2bn(ipcom_buffer_start_ptr(buf), len, bn); if(IP_NULL == bn) return IPCOM_ERR_FAILED; (void)ipcom_buffer_consume(buf, len); *bnp = bn; return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_buffer_put_ssh_mpint *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipssh_buffer_put_ssh_mpint(Ipcom_buffer* buf, Ip_u8* mpint, Ip_size_t len) { /* If most significant bit is set, a zero byte must be added */ if(mpint[0] & 0x80) { if ( ipcom_buffer_put_int(buf, ip_htonl(len+1)) || ipcom_buffer_put_char(buf, 0)) { return IPCOM_ERR_FAILED; } } else { if ( ipcom_buffer_put_int(buf, ip_htonl(len))) { return IPCOM_ERR_FAILED; } } return ipcom_buffer_put(buf, mpint, len); } /* *=========================================================================== * ipssh_buffer_get_int *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipssh_buffer_get_int(Ipcom_buffer* buf, Ip_s32* val) { if(ipcom_buffer_get_int(buf, val)) return IPCOM_ERR_FAILED; *val = ip_ntohl(*val); return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_buffer_get_u64 *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipssh_buffer_get_u64(Ipcom_buffer* buf, Ip_u64* val) { Ip_u32 hi, lo; if(ipcom_buffer_get_int(buf, (Ip_s32*)&hi) || ipcom_buffer_get_int(buf, (Ip_s32*)&lo)) { return IPCOM_ERR_FAILED; } IP_U64_SET(*val, ip_ntohl(hi), ip_ntohl(lo)); return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_buffer_get_u64 *=========================================================================== * Description: * Parameters: * Returns: * */ /* *=========================================================================== * ipssh_buffer_put_u64 *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ip_err ipssh_buffer_put_u64(Ipcom_buffer* buf, Ip_u64 val) { Ip_u32 hi, lo; hi = IP_U64_GETHI(val); lo = IP_U64_GETLO(val); if(ipssh_buffer_put_uint(buf, hi) || ipssh_buffer_put_uint(buf, lo)) { return IPCOM_ERR_FAILED; } return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_append_list *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_append_list(Ipcom_buffer* buf, Ip_u8* alg_ids) { Ip_size_t len; Ip_size_t org_len; Ip_size_t len_pos; char* len_ptr; len_pos = ipcom_buffer_len(buf); org_len = ipcom_buffer_len(buf); if(ipcom_buffer_decr_cap(buf, 4) != IPCOM_SUCCESS) /* Proceed past length field */ return IPCOM_ERR_FAILED; for(;*alg_ids != IPSSH_ID_END; alg_ids++) { const char* str; str = ipssh_alg_id2str(*alg_ids); if(!str || ipcom_buffer_put(buf, (Ip_u8*)str, (Ip_u32)ipcom_strlen(str))) { return IPCOM_ERR_FAILED; } if(alg_ids[1] != IPSSH_ID_END) /* Add a comma if there is an additional item in the list */ { if(ipcom_buffer_put(buf, (Ip_u8*)",", 1)) { return IPCOM_ERR_FAILED; } } } /* Update length */ len = ipcom_buffer_len(buf) - org_len - 4; len_ptr = ipcom_buffer_ptr2pos(buf, len_pos); IP_SET_32ON8(len_ptr, ip_htonl(len)); return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_buffer_put_dsa_key *=========================================================================== * Description: * Parameters: * Returns: */ IP_GLOBAL Ip_err ipssh_buffer_put_dsa_key(Ipcom_buffer* buf, DSA* dsa) { Ip_size_t len; Ip_size_t len_pos; char* len_ptr; len_pos = ipcom_buffer_len(buf); len = ipcom_buffer_len(buf); if(ipcom_buffer_put_int(buf, 0) || ipssh_buffer_put_sshstr(buf, IPSSH_DSA_STR) || ipssh_buffer_put_bn(buf, dsa->p) < 0 || ipssh_buffer_put_bn(buf, dsa->q) < 0 || ipssh_buffer_put_bn(buf, dsa->g) < 0 || ipssh_buffer_put_bn(buf, dsa->pub_key) < 0) { IPCOM_LOG0(ERR, "ipssh_buffer_put_dsa_key :: ipssh_buffer_put_bn() failed"); return IPCOM_ERR_FAILED; } len_ptr = ipcom_buffer_ptr2pos(buf, len_pos); len = ipcom_buffer_len(buf)-len-4; IP_SET_32ON8(len_ptr, (ip_htonl(len))); return IPCOM_SUCCESS; } #ifdef IPSSH_USE_RSA /* *=========================================================================== * ipssh_buffer_put_rsa_key *=========================================================================== * Description: * Parameters: * Returns: */ IP_GLOBAL Ip_err ipssh_buffer_put_rsa_key(Ipcom_buffer* buf, RSA* rsa) { Ip_u32 len; Ip_size_t len_pos; char* len_ptr; len_pos = ipcom_buffer_len(buf); len = (Ip_u32)ipcom_buffer_len(buf); if(ipcom_buffer_put_int(buf, 0) || ipssh_buffer_put_sshstr(buf, IPSSH_RSA_STR) || ipssh_buffer_put_bn(buf, rsa->e) < 0 || ipssh_buffer_put_bn(buf, rsa->n) < 0) { IPCOM_LOG0(ERR, "ipssh_buffer_put_rsa_key :: failed"); return IPCOM_ERR_FAILED; } len_ptr = ipcom_buffer_ptr2pos(buf, len_pos); len = (Ip_u32)(ipcom_buffer_len(buf)-len-4); IP_SET_32ON8(len_ptr, (ip_htonl(len))); return IPCOM_SUCCESS; } #endif /* *=========================================================================== * ipssh_buffer_get_pub_key *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL EVP_PKEY* ipssh_buffer_get_pub_key(Ipcom_buffer* buf) { char* key_type; EVP_PKEY* key = IP_NULL; DSA* dsa_key = IP_NULL; RSA* rsa_key = IP_NULL; Ip_s32 blob_len; /* Read length of key blob */ if(ipssh_buffer_get_int(buf, &blob_len) != IPCOM_SUCCESS) return IP_NULL; key_type = ipssh_buffer_get_sshstr(buf); if(!key_type) return IP_NULL; if(ipcom_strcmp(key_type, IPSSH_DSA_STR) == 0) { dsa_key = DSA_new(); if(!dsa_key) goto cleanup; if(ipssh_buffer_get_ssh_mpint2(buf, &dsa_key->p) != IPCOM_SUCCESS || ipssh_buffer_get_ssh_mpint2(buf, &dsa_key->q) != IPCOM_SUCCESS || ipssh_buffer_get_ssh_mpint2(buf, &dsa_key->g) != IPCOM_SUCCESS || ipssh_buffer_get_ssh_mpint2(buf, &dsa_key->pub_key) != IPCOM_SUCCESS) { goto cleanup; } key = EVP_PKEY_new(); if(!key) goto cleanup; if(!EVP_PKEY_assign_DSA(key, dsa_key)) { EVP_PKEY_free(key); key = IP_NULL; goto cleanup; } (void)DSA_up_ref(dsa_key); } #ifdef IPSSH_USE_RSA else if(ipcom_strcmp(key_type, IPSSH_RSA_STR) == 0) { rsa_key = RSA_new(); if(!rsa_key) goto cleanup; if(ipssh_buffer_get_ssh_mpint2(buf, &rsa_key->e) != IPCOM_SUCCESS || ipssh_buffer_get_ssh_mpint2(buf, &rsa_key->n) != IPCOM_SUCCESS) { goto cleanup; } key = EVP_PKEY_new(); if(!key) goto cleanup; if(!EVP_PKEY_assign_RSA(key, rsa_key)) { EVP_PKEY_free(key); key = IP_NULL; goto cleanup; } (void)RSA_up_ref(rsa_key); } #endif /* IPSSH_USE_RSA */ else goto cleanup; cleanup: ipcom_free(key_type); if(rsa_key) RSA_free(rsa_key); if(dsa_key) DSA_free(dsa_key); return key; } /* *=========================================================================== * ipssh_buffer_put_pub_key *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ip_err ipssh_buffer_put_pub_key(Ipcom_buffer* buf, EVP_PKEY* key) { Ip_size_t len_pos; Ip_u32 blob_len; switch(key->type) { case NID_dsa: len_pos = ipcom_buffer_len(buf); if(ipssh_buffer_put_int(buf, 0) /* Dummy key blob length */ ||ipssh_buffer_put_sshstr(buf, IPSSH_DSA_STR)) { return IPCOM_ERR_FAILED; } if(ipssh_buffer_put_bn(buf, key->pkey.dsa->p)<0 ||ipssh_buffer_put_bn(buf, key->pkey.dsa->q)<0 ||ipssh_buffer_put_bn(buf, key->pkey.dsa->g)<0 ||ipssh_buffer_put_bn(buf, key->pkey.dsa->pub_key)<0) { return IPCOM_ERR_FAILED; } blob_len = (Ip_u32)(ipcom_buffer_len(buf) - len_pos - 4); IP_SET_32ON8(ipcom_buffer_ptr2pos(buf, len_pos), ip_htonl(blob_len)); break; #ifdef IPSSH_USE_RSA case NID_rsaEncryption: len_pos = ipcom_buffer_len(buf); if(ipssh_buffer_put_int(buf, 0) /* Dummy key blob length */ || ipssh_buffer_put_sshstr(buf, IPSSH_RSA_STR)) { return IPCOM_ERR_FAILED; } if (ipssh_buffer_put_bn(buf, key->pkey.rsa->e) <0 || ipssh_buffer_put_bn(buf, key->pkey.rsa->n) <0) return IPCOM_ERR_FAILED; blob_len = (Ip_u32)(ipcom_buffer_len(buf) - len_pos - 4); IP_SET_32ON8(ipcom_buffer_ptr2pos(buf, len_pos), ip_htonl(blob_len)); break; #endif default: IP_PANIC(); } return IPCOM_SUCCESS; } IP_GLOBAL Ip_err ipssh_buffer_put_dsa_sig(Ipcom_buffer* buf, DSA_SIG* dsa_sig) { Ip_s32 r_len; Ip_s32 s_len; int i; int pad; if (ipcom_buffer_capacity(buf) < (4+ipcom_strlen(IPSSH_DSA_STR)+4+2*IPSSH_SIG_LEN)) return IPCOM_ERR_INVALID_ARG; r_len = BN_num_bytes(dsa_sig->r); s_len = BN_num_bytes(dsa_sig->s); if (r_len > IPSSH_MAX_INT_LEN || s_len > IPSSH_MAX_INT_LEN) { IPCOM_LOG2(ERR, "ipssh_buffer_put_dsa_sig :: bad sig size %d %d", r_len, s_len); return IPCOM_ERR_INVALID_ARG; } for(i=0,pad=IPSSH_SIG_LEN-r_len; ir, ipcom_buffer_end_ptr(buf)); (void)ipcom_buffer_decr_cap(buf, r_len); for(i=0,pad=IPSSH_SIG_LEN-s_len; is, ipcom_buffer_end_ptr(buf)); (void)ipcom_buffer_decr_cap(buf, s_len); return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_buffer_put_bn_v1 *=========================================================================== * Description: Store a multiprecision integer in SSH v1 format * Parameters: * Returns: */ IP_GLOBAL Ip_err ipssh_buffer_put_bn_v1(Ipcom_buffer* buf, BIGNUM* bn) { Ip_u16 bits; int len; Ip_u8* ptr; bits = (Ip_u16) BN_num_bits(bn); len = BN_num_bytes(bn); if((int)ipcom_buffer_capacity(buf) < (len+2)) { IPCOM_LOG2(ERR, "ipssh_buffer_put_bn_v1 :: buffer too small, buf size=%d, needed=%d", ipcom_buffer_capacity(buf), len+2); return IPCOM_ERR_FAILED; } ptr = ipcom_buffer_end_ptr(buf); IPSSH_SET_16_PTR(ptr, bits); (void)ipcom_buffer_decr_cap(buf, 2); if(BN_bn2bin(bn, ipcom_buffer_end_ptr(buf)) < 0) { IPCOM_LOG0(ERR, "ipssh_buffer_put_bn_v1 :: BN_bn2bin() failed"); return IPCOM_ERR_FAILED; } return ipcom_buffer_decr_cap(buf, len); } /* *=========================================================================== * ipssh_buffer_put_bn2_v1 *=========================================================================== * Description: Store a multiprecision integer in SSH v1 format * Parameters: * Returns: */ IP_GLOBAL Ip_err ipssh_buffer_put_bn2_v1(Ipcom_buffer* buf, Ip_u8* value, Ip_u16 bits) { int len; Ip_u8* ptr; len = (bits+7)/8; if(ipcom_buffer_capacity(buf) < ((Ip_u32)(len+2))) { IPCOM_LOG2(ERR, "ipssh_buffer_put_bn_v1 :: buffer too small, buf size=%d, needed=%d", ipcom_buffer_capacity(buf), len+2); return IPCOM_ERR_FAILED; } ptr = ipcom_buffer_end_ptr(buf); IPSSH_SET_16_PTR(ptr, bits); (void)ipcom_buffer_decr_cap(buf, 2); if(ipcom_buffer_put(buf, value, len) != IPCOM_SUCCESS) { IPCOM_LOG0(ERR, "ipssh_buffer_put_bn2_v1 :: ipcom_buffer_put() failed"); return IPCOM_ERR_FAILED; } return IPCOM_SUCCESS; } /* *=========================================================================== * ipssh_buffer_new_v1_pkt *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ipcom_buffer* ipssh_buffer_new_v1_pkt(Ip_size_t size, Ip_u8 pkt_type) { Ipcom_buffer* pkt; pkt = ipcom_buffer_new(size); if(!pkt) return IP_NULL; (void)ipcom_buffer_decr_cap(pkt, 12); /* make room for length and padding fields */ (void)ipcom_buffer_put_char(pkt, pkt_type); return pkt; } /* *=========================================================================== * ipssh_buffer_new_v2_pkt *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL Ipcom_buffer* ipssh_buffer_new_v2_pkt(Ip_size_t size, Ip_u8 pkt_type) { Ipcom_buffer* pkt; pkt = ipcom_buffer_new(size + 5 + IPSSH_MAX_PAD); if(!pkt) return IP_NULL; (void)ipcom_buffer_decr_cap(pkt, 5); /* make room for packet length and pad length fields */ (void)ipcom_buffer_put_char(pkt, pkt_type); return pkt; } /* *=========================================================================== * ipssh_buffer_get_v1_bn *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL BIGNUM* ipssh_buffer_get_v1_bn(Ipcom_buffer* buf) { BIGNUM* bn; Ip_u16 bits; Ip_u32 bytes; if(ipcom_buffer_get(buf, (Ip_u8*)&bits, 2)) return IP_NULL; bits = ip_ntohs(bits); bytes = (bits+7) / 8; if(bytes>ipcom_buffer_len(buf)) return IP_NULL; bn = BN_bin2bn(ipcom_buffer_start_ptr(buf), bytes, IP_NULL); if(IP_NULL == bn) return IP_NULL; (void)ipcom_buffer_consume(buf, bytes); return bn; } /* *=========================================================================== * ipssh_buffer_new_sftp_pkt *=========================================================================== * Description: An sftp packet looks as follows: * L1 P T ID L2 L3 M P * | | | | | | | | * transport +----+-+-+ * channel +----+----+ * sftp +----+-+---- * * L1 - Length of packet (4 bytes) * P - Pad length (1 byte) * T - Packet type (Always CHANNEL_DATA) (1 byte) * ID - Channel ID (4 bytes) * L2 - Length of CHANNEL_DATA message (4 bytes) * L3 - Length of sftp message (4bytes) * M - sftp message type (1 byte) * P - Payload of sftp message * Parameters: * Returns: * */ IP_PUBLIC Ipcom_buffer* ipssh_buffer_new_sftp_pkt(Ip_size_t sftp_msg_size, Ip_u8 sftp_msg_type) { Ipcom_buffer* pkt; pkt = ipcom_buffer_new(sftp_msg_size+6+8+5+IPSSH_MAX_PAD); /* transport overhead, channel overhead, sftp overhead, max padding */ if(!pkt) return IP_NULL; (void)ipcom_buffer_decr_cap(pkt, 18); (void)ipcom_buffer_put_char(pkt, sftp_msg_type); return pkt; } /* *=========================================================================== * ipssh_buffer_new_channel_data_pkt *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC Ipcom_buffer* ipssh_buffer_new_channel_data_pkt(Ip_size_t pkt_size, Ipssh_channel* channel) { Ipcom_buffer* pkt; (void) channel; pkt = ipcom_buffer_new(pkt_size+6+8+IPSSH_MAX_PAD); /* transport overhead, channel overhead, max padding */ if(!pkt) return IP_NULL; (void)ipcom_buffer_decr_cap(pkt, 14); return pkt; } /* **************************************************************************** * END OF FILE **************************************************************************** */