Esempio n. 1
0
/* {{{ libssh2_packet_read
 * Collect a packet into the input brigade
 * block only controls whether or not to wait for a packet to start,
 * Once a packet starts, libssh2 will block until it is complete
 * Returns packet type added to input brigade (0 if nothing added), or -1 on failure
 */
int libssh2_packet_read(LIBSSH2_SESSION *session, int should_block)
{
	int packet_type = -1;

	if (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) {
		return 0;
	}

#ifndef WIN32
	fcntl(session->socket_fd, F_SETFL, O_NONBLOCK);
#else
	{
		u_long non_block = TRUE;
		ioctlsocket(session->socket_fd, FIONBIO, &non_block);
	}
#endif

#ifdef LIBSSH2_DEBUG_TRANSPORT
	_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Checking for packet: will%s block", should_block ? "" : " not");
#endif
	if (session->state & LIBSSH2_STATE_NEWKEYS) {
		/* Temporary Buffer
		 * The largest blocksize (currently) is 32, the largest MAC (currently) is 20
		 */
		unsigned char block[2 * 32], *payload, *s, tmp[6];
		long read_len;
		unsigned long blocksize = session->remote.crypt->blocksize;
		unsigned long packet_len, payload_len;
		int padding_len;
		int macstate;
		int free_payload = 1;
		/* Safely ignored in CUSTOM cipher mode */
		EVP_CIPHER_CTX *ctx = (EVP_CIPHER_CTX *)session->remote.crypt_abstract;

		/* Note: If we add any cipher with a blocksize less than 6 we'll need to get more creative with this
		 * For now, all blocksize sizes are 8+
		 */
		if (should_block) {
			read_len = libssh2_blocking_read(session, block, blocksize);
		} else {
			read_len = recv(session->socket_fd, block, 1, LIBSSH2_SOCKET_RECV_FLAGS(session));
			if (read_len <= 0) {
				return 0;
			}
			read_len += libssh2_blocking_read(session, block + read_len, blocksize - read_len);
		}
		if (read_len < blocksize) {
			return (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) ? 0 : -1;
		}

		if (session->remote.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) {
			EVP_Cipher(ctx, block + blocksize, block, blocksize);
			memcpy(block, block + blocksize, blocksize);
		} else {
			if (session->remote.crypt->crypt(session, block, &session->remote.crypt_abstract)) {
				libssh2_error(session, LIBSSH2_ERROR_DECRYPT, "Error decrypting packet preamble", 0);
				return -1;
			}
		}

		packet_len = libssh2_ntohu32(block);
		padding_len = block[4];
#ifdef LIBSSH2_DEBUG_TRANSPORT
	_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Processing packet %lu bytes long (with %lu bytes padding)", packet_len, padding_len);
#endif
		memcpy(tmp, block, 5); /* Use this for MAC later */

		payload_len = packet_len - 1; /* padding_len(1) */
		/* Sanity Check */
		if ((payload_len > LIBSSH2_PACKET_MAXPAYLOAD) ||
			((packet_len + 4) % blocksize)) {
			/* If something goes horribly wrong during the decryption phase, just bailout and die gracefully */
			session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
			libssh2_error(session, LIBSSH2_ERROR_PROTO, "Fatal protocol error, invalid payload size", 0);
			return -1;
		}

		s = payload = LIBSSH2_ALLOC(session, payload_len);
		memcpy(s, block + 5, blocksize - 5);
		s += blocksize - 5;

		while ((s - payload) < payload_len) {
			read_len = libssh2_blocking_read(session, block, blocksize);
			if (read_len < blocksize) {
				LIBSSH2_FREE(session, payload);
				return -1;
			}
			if (session->remote.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) {
				EVP_Cipher(ctx, block + blocksize, block, blocksize);
				memcpy(s, block + blocksize, blocksize);
			} else {
				if (session->remote.crypt->crypt(session, block, &session->remote.crypt_abstract)) {
					libssh2_error(session, LIBSSH2_ERROR_DECRYPT, "Error decrypting packet preamble", 0);
					LIBSSH2_FREE(session, payload);
					return -1;
				}
				memcpy(s, block, blocksize);
			}

			s += blocksize;
		}

		read_len = libssh2_blocking_read(session, block, session->remote.mac->mac_len);
		if (read_len < session->remote.mac->mac_len) {
			LIBSSH2_FREE(session, payload);
			return -1;
		}

		/* Calculate MAC hash */
 		session->remote.mac->hash(session, block + session->remote.mac->mac_len, session->remote.seqno, tmp, 5, payload, payload_len, &session->remote.mac_abstract);

		macstate =  (strncmp(block, block + session->remote.mac->mac_len, session->remote.mac->mac_len) == 0) ? LIBSSH2_MAC_CONFIRMED : LIBSSH2_MAC_INVALID;

		session->remote.seqno++;

		/* Ignore padding */
		payload_len -= padding_len;

		if (session->remote.comp &&
			strcmp(session->remote.comp->name, "none")) {
			/* Decompress */
			unsigned char *data;
			unsigned long data_len;

			if (session->remote.comp->comp(session, 0, &data, &data_len, LIBSSH2_PACKET_MAXDECOMP, &free_payload, payload, payload_len, &session->remote.comp_abstract)) {
				LIBSSH2_FREE(session, payload);
				return -1;
			}
#ifdef LIBSSH2_DEBUG_TRANSPORT
	_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Payload decompressed: %lu bytes(compressed) to %lu bytes(uncompressed)", data_len, payload_len);
#endif
			if (free_payload) {
				LIBSSH2_FREE(session, payload);
				payload = data;
				payload_len = data_len;
			} else {
				if (data == payload) {
					/* It's not to be freed, because the compression layer reused payload,
					 * So let's do the same!
					 */
					payload_len = data_len;
				} else {
					/* No comp_method actually lets this happen, but let's prepare for the future */

					LIBSSH2_FREE(session, payload);

					/* We need a freeable struct otherwise the brigade won't know what to do with it */
					payload = LIBSSH2_ALLOC(session, data_len);
					if (!payload) {
						libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for copy of uncompressed data", 0);
						return -1;
					}
					memcpy(payload, data, data_len);
					payload_len = data_len;
				}
			}
		}

		packet_type = payload[0];
		libssh2_packet_add(session, payload, payload_len, macstate);

	} else { /* No cipher active */
		unsigned char *payload;
		unsigned char buf[24];
		unsigned long buf_len, payload_len;
		unsigned long packet_length;
		unsigned long padding_length;

		if (should_block) {
			buf_len = libssh2_blocking_read(session, buf, 5);
		} else {
			buf_len = recv(session->socket_fd, buf, 1, LIBSSH2_SOCKET_RECV_FLAGS(session));
			if (buf_len <= 0) {
				return 0;
			}
			buf_len += libssh2_blocking_read(session, buf, 5 - buf_len);
		}
		if (buf_len < 5) {
			/* Something bad happened */
			return -1;
		}
		packet_length = libssh2_ntohu32(buf);
		padding_length = buf[4];
#ifdef LIBSSH2_DEBUG_TRANSPORT
	_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Processing plaintext packet %lu bytes long (with %lu bytes padding)", packet_length, padding_length);
#endif

		payload_len = packet_length - padding_length - 1; /* padding_length(1) */
		payload = LIBSSH2_ALLOC(session, payload_len);
		if (!payload) {
			libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for copy of plaintext data", 0);
			return -1;
		}

		if (libssh2_blocking_read(session, payload, payload_len) < payload_len) {
			return (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) ? 0 : -1;
		}
		while (padding_length) {
			int l;
			/* Flush padding */
			l = libssh2_blocking_read(session, buf, padding_length);
			if (l > 0)
				padding_length -= l;
			else
				break;
		}

		packet_type = payload[0];

		/* MACs don't exist in non-encrypted mode */
		libssh2_packet_add(session, payload, payload_len, LIBSSH2_MAC_CONFIRMED);
		session->remote.seqno++;
	}
	return packet_type;
}
Esempio n. 2
0
/*
 * fullpacket() gets called when a full packet has been received and properly
 * collected.
 */
static libssh2pack_t
fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ )
{
    unsigned char macbuf[MAX_MACSIZE];
    struct transportpacket *p = &session->packet;
    int rc;

    if (session->fullpacket_state == libssh2_NB_state_idle) {
        session->fullpacket_macstate = LIBSSH2_MAC_CONFIRMED;
        session->fullpacket_payload_len = p->packet_length - 1;

        if (encrypted) {

            /* Calculate MAC hash */
            session->remote.mac->hash(session, macbuf,  /* store hash here */
                                      session->remote.seqno,
                                      p->init, 5,
                                      p->payload,
                                      session->fullpacket_payload_len,
                                      &session->remote.mac_abstract);

            /* Compare the calculated hash with the MAC we just read from
             * the network. The read one is at the very end of the payload
             * buffer. Note that 'payload_len' here is the packet_length
             * field which includes the padding but not the MAC.
             */
            if (memcmp(macbuf, p->payload + session->fullpacket_payload_len,
                       session->remote.mac->mac_len)) {
                session->fullpacket_macstate = LIBSSH2_MAC_INVALID;
            }
        }

        session->remote.seqno++;

        /* ignore the padding */
        session->fullpacket_payload_len -= p->padding_length;

        /* Check for and deal with decompression */
        if (session->remote.comp && strcmp(session->remote.comp->name, "none")) {
            unsigned char *data;
            unsigned long data_len;
            int free_payload = 1;

            if (session->remote.comp->comp(session, 0,
                                           &data, &data_len,
                                           LIBSSH2_PACKET_MAXDECOMP,
                                           &free_payload,
                                           p->payload,
                                           session->fullpacket_payload_len,
                                           &session->remote.comp_abstract)) {
                LIBSSH2_FREE(session, p->payload);
                return PACKET_FAIL;
            }

            if (free_payload) {
                LIBSSH2_FREE(session, p->payload);
                p->payload = data;
                session->fullpacket_payload_len = data_len;
            } else {
                if (data == p->payload) {
                    /* It's not to be freed, because the
                     * compression layer reused payload, So let's
                     * do the same!
                     */
                    session->fullpacket_payload_len = data_len;
                } else {
                    /* No comp_method actually lets this happen,
                     * but let's prepare for the future */

                    LIBSSH2_FREE(session, p->payload);

                    /* We need a freeable struct otherwise the
                     * brigade won't know what to do with it */
                    p->payload = LIBSSH2_ALLOC(session, data_len);
                    if (!p->payload) {
                        libssh2_error(session, LIBSSH2_ERROR_ALLOC, (char *)
                                      "Unable to allocate memory for copy of uncompressed data",
                                      0);
                        return PACKET_ENOMEM;
                    }
                    memcpy(p->payload, data, data_len);
                    session->fullpacket_payload_len = data_len;
                }
            }
        }

        session->fullpacket_packet_type = p->payload[0];

        debugdump(session, "libssh2_packet_read() plain",
                  p->payload, session->fullpacket_payload_len);

        session->fullpacket_state = libssh2_NB_state_created;
    }

    if (session->fullpacket_state == libssh2_NB_state_created) {
        rc = libssh2_packet_add(session, p->payload,
                                session->fullpacket_payload_len,
                                session->fullpacket_macstate);
        if (rc == PACKET_EAGAIN) {
            return PACKET_EAGAIN;
        } else if (rc < 0) {
            return PACKET_FAIL;
        }
    }

    session->fullpacket_state = libssh2_NB_state_idle;

    return session->fullpacket_packet_type;
}