Exemple #1
0
/* {{{ 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 */
}
Exemple #2
0
/* {{{ 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;
}