/* {{{ libssh2_packet_write * Send a packet, encrypting it and adding a MAC code if necessary * Returns 0 on success, non-zero on failure. * * Returns PACKET_EAGAIN if it would block - and if it does so, you should * call this function again as soon as it is likely that more data can be * sent, and this function should then be called with the same argument set * (same data pointer and same data_len) until zero or failure is returned. */ int libssh2_packet_write(LIBSSH2_SESSION * session, unsigned char *data, unsigned long data_len) { int blocksize = (session->state & LIBSSH2_STATE_NEWKEYS) ? session->local.crypt-> blocksize : 8; int padding_length; int packet_length; int total_length; int free_data = 0; #ifdef RANDOM_PADDING int rand_max; int seed = data[0]; /* FIXME: make this random */ #endif struct transportpacket *p = &session->packet; int encrypted; int i; ssize_t ret; libssh2pack_t rc; unsigned char *orgdata = data; unsigned long orgdata_len = data_len; debugdump(session, "libssh2_packet_write plain", data, data_len); /* FIRST, check if we have a pending write to complete */ rc = send_existing(session, data, data_len, &ret); if (rc || ret) { return rc; } encrypted = (session->state & LIBSSH2_STATE_NEWKEYS) ? 1 : 0; /* check if we should compress */ if (encrypted && strcmp(session->local.comp->name, "none")) { if (session->local.comp-> comp(session, 1, &data, &data_len, LIBSSH2_PACKET_MAXCOMP, &free_data, data, data_len, &session->local.comp_abstract)) { return PACKET_COMPRESS; /* compression failure */ } } /* RFC4253 says: Note that the length of the concatenation of 'packet_length', 'padding_length', 'payload', and 'random padding' MUST be a multiple of the cipher block size or 8, whichever is larger. */ /* Plain math: (4 + 1 + packet_length + padding_length) % blocksize == 0 */ packet_length = data_len + 1 + 4; /* 1 is for padding_length field 4 for the packet_length field */ /* at this point we have it all except the padding */ /* first figure out our minimum padding amount to make it an even block size */ padding_length = blocksize - (packet_length % blocksize); /* if the padding becomes too small we add another blocksize worth of it (taken from the original libssh2 where it didn't have any real explanation) */ if (padding_length < 4) { padding_length += blocksize; } #ifdef RANDOM_PADDING /* FIXME: we can add padding here, but that also makes the packets bigger etc */ /* now we can add 'blocksize' to the padding_length N number of times (to "help thwart traffic analysis") but it must be less than 255 in total */ rand_max = (255 - padding_length) / blocksize + 1; padding_length += blocksize * (seed % rand_max); #endif packet_length += padding_length; /* append the MAC length to the total_length size */ total_length = packet_length + (encrypted ? session->local.mac->mac_len : 0); /* allocate memory to store the outgoing packet in, in case we can't send the whole one and thus need to keep it after this function returns. */ p->outbuf = LIBSSH2_ALLOC(session, total_length); if (!p->outbuf) { return PACKET_ENOMEM; } /* store packet_length, which is the size of the whole packet except the MAC and the packet_length field itself */ libssh2_htonu32(p->outbuf, packet_length - 4); /* store padding_length */ p->outbuf[4] = padding_length; /* copy the payload data */ memcpy(p->outbuf + 5, data, data_len); /* fill the padding area with random junk */ libssh2_random(p->outbuf + 5 + data_len, padding_length); if (free_data) { LIBSSH2_FREE(session, data); } if (encrypted) { /* Calculate MAC hash. Put the output at index packet_length, since that size includes the whole packet. The MAC is calculated on the entire unencrypted packet, including all fields except the MAC field itself. */ session->local.mac->hash(session, p->outbuf + packet_length, session->local.seqno, p->outbuf, packet_length, NULL, 0, &session->local.mac_abstract); /* Encrypt the whole packet data, one block size at a time. The MAC field is not encrypted. */ for(i = 0; i < packet_length; i += session->local.crypt->blocksize) { unsigned char *ptr = &p->outbuf[i]; if (session->local.crypt-> crypt(session, ptr, &session->local.crypt_abstract)) return PACKET_FAIL; /* encryption failure */ } } session->local.seqno++; ret = send(session->socket_fd, p->outbuf, total_length, LIBSSH2_SOCKET_SEND_FLAGS(session)); if (ret != -1) { debugdump(session, "libssh2_packet_write send()", p->outbuf, ret); } if (ret != total_length) { if ((ret > 0) || ((ret == -1) && (errno == EAGAIN))) { /* the whole packet could not be sent, save the rest */ p->odata = orgdata; p->olen = orgdata_len; p->osent = (ret == -1) ? 0 : ret; p->ototal_num = total_length; return PACKET_EAGAIN; } return PACKET_FAIL; } /* the whole thing got sent away */ p->odata = NULL; p->olen = 0; LIBSSH2_FREE(session, p->outbuf); p->outbuf = NULL; return PACKET_NONE; /* all is good */ }
/* {{{ libssh2_kexinit * Send SSH_MSG_KEXINIT packet */ static int libssh2_kexinit(LIBSSH2_SESSION *session) { size_t data_len = 62; /* packet_type(1) + cookie(16) + first_packet_follows(1) + reserved(4) + length longs(40) */ size_t kex_len, hostkey_len = 0; size_t crypt_cs_len, crypt_sc_len; size_t comp_cs_len, comp_sc_len; size_t mac_cs_len, mac_sc_len; size_t lang_cs_len, lang_sc_len; unsigned char *data, *s; kex_len = LIBSSH2_METHOD_PREFS_LEN(session->kex_prefs, libssh2_kex_methods); hostkey_len = LIBSSH2_METHOD_PREFS_LEN(session->hostkey_prefs, libssh2_hostkey_methods()); crypt_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.crypt_prefs, libssh2_crypt_methods()); crypt_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.crypt_prefs, libssh2_crypt_methods()); mac_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.mac_prefs, libssh2_mac_methods()); mac_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.mac_prefs, libssh2_mac_methods()); comp_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.comp_prefs, libssh2_comp_methods()); comp_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.comp_prefs, libssh2_comp_methods()); lang_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.lang_prefs, NULL); lang_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.lang_prefs, NULL); data_len += kex_len + hostkey_len + \ crypt_cs_len + crypt_sc_len + \ comp_cs_len + comp_sc_len + \ mac_cs_len + mac_sc_len + \ lang_cs_len + lang_sc_len; s = data = LIBSSH2_ALLOC(session, data_len); if (!data) { libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory", 0); return -1; } *(s++) = SSH_MSG_KEXINIT; libssh2_random(s, 16); s += 16; /* Ennumerating through these lists twice is probably (certainly?) inefficient from a CPU standpoint, but it saves multiple malloc/realloc calls */ LIBSSH2_METHOD_PREFS_STR(s, kex_len, session->kex_prefs, libssh2_kex_methods); LIBSSH2_METHOD_PREFS_STR(s, hostkey_len, session->hostkey_prefs, libssh2_hostkey_methods()); LIBSSH2_METHOD_PREFS_STR(s, crypt_cs_len, session->local.crypt_prefs, libssh2_crypt_methods()); LIBSSH2_METHOD_PREFS_STR(s, crypt_sc_len, session->remote.crypt_prefs, libssh2_crypt_methods()); LIBSSH2_METHOD_PREFS_STR(s, mac_cs_len, session->local.mac_prefs, libssh2_mac_methods()); LIBSSH2_METHOD_PREFS_STR(s, mac_sc_len, session->remote.mac_prefs, libssh2_mac_methods()); LIBSSH2_METHOD_PREFS_STR(s, comp_cs_len, session->local.comp_prefs, libssh2_comp_methods()); LIBSSH2_METHOD_PREFS_STR(s, comp_sc_len, session->remote.comp_prefs, libssh2_comp_methods()); LIBSSH2_METHOD_PREFS_STR(s, lang_cs_len, session->local.lang_prefs, NULL); LIBSSH2_METHOD_PREFS_STR(s, lang_sc_len, session->remote.lang_prefs, NULL); /* No optimistic KEX packet follows */ /* Deal with optimistic packets * session->flags |= KEXINIT_OPTIMISTIC * session->flags |= KEXINIT_METHODSMATCH */ *(s++) = 0; /* Reserved == 0 */ *(s++) = 0; *(s++) = 0; *(s++) = 0; *(s++) = 0; #ifdef LIBSSH2_DEBUG_KEX { /* Funnily enough, they'll all "appear" to be '\0' terminated */ char *p = data + 21; /* type(1) + cookie(16) + len(4) */ _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent KEX: %s", p); p += kex_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent HOSTKEY: %s", p); p += hostkey_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent CRYPT_CS: %s", p); p += crypt_cs_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent CRYPT_SC: %s", p); p += crypt_sc_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent MAC_CS: %s", p); p += mac_cs_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent MAC_SC: %s", p); p += mac_sc_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent COMP_CS: %s", p); p += comp_cs_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent COMP_SC: %s", p); p += comp_sc_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent LANG_CS: %s", p); p += lang_cs_len + 4; _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent LANG_SC: %s", p); p += lang_sc_len + 4; } #endif /* LIBSSH2_DEBUG_KEX */ if (libssh2_packet_write(session, data, data_len)) { LIBSSH2_FREE(session, data); libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send KEXINIT packet to remote host", 0); return -1; } if (session->local.kexinit) { LIBSSH2_FREE(session, session->local.kexinit); } session->local.kexinit = data; session->local.kexinit_len = data_len; return 0; }