Exemple #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;
}
Exemple #2
0
/*
 * This function reads the binary stream as specified in chapter 6 of RFC4253
 * "The Secure Shell (SSH) Transport Layer Protocol"
 */
libssh2pack_t
libssh2_packet_read(LIBSSH2_SESSION * session)
{
    libssh2pack_t rc;
    struct transportpacket *p = &session->packet;
    int remainbuf;
    int remainpack;
    int numbytes;
    int numdecrypt;
    unsigned char block[MAX_BLOCKSIZE];
    int blocksize;
    int encrypted = 1;

    /*
     * =============================== NOTE ===============================
     * I know this is very ugly and not a really good use of "goto", but
     * this case statement would be even uglier to do it any other way
     */
    if (session->readPack_state == libssh2_NB_state_jump1) {
        session->readPack_state = libssh2_NB_state_idle;
        encrypted = session->readPack_encrypted;
        goto libssh2_packet_read_point1;
    }

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

        if (session->state & LIBSSH2_STATE_NEWKEYS) {
            blocksize = session->remote.crypt->blocksize;
        } else {
            encrypted = 0;      /* not encrypted */
            blocksize = 5;      /* not strictly true, but we can use 5 here to
                                   make the checks below work fine still */
        }

        /* read/use a whole big chunk into a temporary area stored in
           the LIBSSH2_SESSION struct. We will decrypt data from that
           buffer into the packet buffer so this temp one doesn't have
           to be able to keep a whole SSH packet, just be large enough
           so that we can read big chunks from the network layer. */

        /* how much data there is remaining in the buffer to deal with
           before we should read more from the network */
        remainbuf = p->writeidx - p->readidx;

        /* if remainbuf turns negative we have a bad internal error */
        assert(remainbuf >= 0);

        if (remainbuf < blocksize) {
            /* If we have less than a blocksize left, it is too
               little data to deal with, read more */
            ssize_t nread;

            /* move any remainder to the start of the buffer so
               that we can do a full refill */
            if (remainbuf) {
                memmove(p->buf, &p->buf[p->readidx], remainbuf);
                p->readidx = 0;
                p->writeidx = remainbuf;
            } else {
                /* nothing to move, just zero the indexes */
                p->readidx = p->writeidx = 0;
            }

            /* now read a big chunk from the network into the temp buffer */
            nread =
                recv(session->socket_fd, &p->buf[remainbuf],
                     PACKETBUFSIZE - remainbuf,
                     LIBSSH2_SOCKET_RECV_FLAGS(session));
            if (nread <= 0) {
                /* check if this is due to EAGAIN and return the special
                   return code if so, error out normally otherwise */
#ifdef WIN32
                switch (WSAGetLastError()) {
                case WSAEWOULDBLOCK:
                    errno = EAGAIN;
                    break;

                case WSAENOTSOCK:
                    errno = EBADF;
                    break;

                case WSAENOTCONN:
                case WSAECONNABORTED:
                    errno = WSAENOTCONN;
                    break;

                case WSAEINTR:
                    errno = EINTR;
                    break;
                }
#endif /* WIN32 */
                if ((nread < 0) && (errno == EAGAIN)) {
                    return PACKET_EAGAIN;
                }
                return PACKET_FAIL;
            }
            debugdump(session, "libssh2_packet_read() raw",
                      &p->buf[remainbuf], nread);
            /* advance write pointer */
            p->writeidx += nread;

            /* update remainbuf counter */
            remainbuf = p->writeidx - p->readidx;
        }

        /* how much data to deal with from the buffer */
        numbytes = remainbuf;

        if (!p->total_num) {
            /* No payload package area allocated yet. To know the
               size of this payload, we need to decrypt the first
               blocksize data. */

            if (numbytes < blocksize) {
                /* we can't act on anything less than blocksize, but this
                   check is only done for the initial block since once we have
                   got the start of a block we can in fact deal with fractions
                */
                return PACKET_EAGAIN;
            }

            if (encrypted) {
                rc = decrypt(session, &p->buf[p->readidx], block, blocksize);
                if (rc != PACKET_NONE) {
                    return rc;
                }
                /* save the first 5 bytes of the decrypted package, to be
                   used in the hash calculation later down. */
                memcpy(p->init, &p->buf[p->readidx], 5);
            } else {
                /* the data is plain, just copy it verbatim to
                   the working block buffer */
                memcpy(block, &p->buf[p->readidx], blocksize);
            }

            /* advance the read pointer */
            p->readidx += blocksize;

            /* we now have the initial blocksize bytes decrypted,
             * and we can extract packet and padding length from it
             */
            p->packet_length = libssh2_ntohu32(block);
            p->padding_length = block[4];

            /* total_num is the number of bytes following the initial
               (5 bytes) packet length and padding length fields */
            p->total_num =
                p->packet_length - 1 +
                (encrypted ? session->remote.mac->mac_len : 0);

            /* RFC4253 section 6.1 Maximum Packet Length says:
             *
             * "All implementations MUST be able to process
             * packets with uncompressed payload length of 32768
             * bytes or less and total packet size of 35000 bytes
             * or less (including length, padding length, payload,
             * padding, and MAC.)."
             */
            if (p->total_num > LIBSSH2_PACKET_MAXPAYLOAD) {
                return PACKET_TOOBIG;
            }

            /* Get a packet handle put data into. We get one to
               hold all data, including padding and MAC. */
            p->payload = LIBSSH2_ALLOC(session, p->total_num);
            if (!p->payload) {
                return PACKET_ENOMEM;
            }
            /* init write pointer to start of payload buffer */
            p->wptr = p->payload;

            if (blocksize > 5) {
                /* copy the data from index 5 to the end of
                   the blocksize from the temporary buffer to
                   the start of the decrypted buffer */
                memcpy(p->wptr, &block[5], blocksize - 5);
                p->wptr += blocksize - 5;       /* advance write pointer */
            }

            /* init the data_num field to the number of bytes of
               the package read so far */
            p->data_num = p->wptr - p->payload;

            /* we already dealt with a blocksize worth of data */
            numbytes -= blocksize;
        }

        /* how much there is left to add to the current payload
           package */
        remainpack = p->total_num - p->data_num;

        if (numbytes > remainpack) {
            /* if we have more data in the buffer than what is going into this
               particular packet, we limit this round to this packet only */
            numbytes = remainpack;
        }

        if (encrypted) {
            /* At the end of the incoming stream, there is a MAC,
               and we don't want to decrypt that since we need it
               "raw". We MUST however decrypt the padding data
               since it is used for the hash later on. */
            int skip = session->remote.mac->mac_len;

            /* if what we have plus numbytes is bigger than the
               total minus the skip margin, we should lower the
               amount to decrypt even more */
            if ((p->data_num + numbytes) > (p->total_num - skip)) {
                numdecrypt = (p->total_num - skip) - p->data_num;
            } else {
                int frac;
                numdecrypt = numbytes;
                frac = numdecrypt % blocksize;
                if (frac) {
                    /* not an aligned amount of blocks,
                       align it */
                    numdecrypt -= frac;
                    /* and make it no unencrypted data
                       after it */
                    numbytes = 0;
                }
            }
        } else {
            /* unencrypted data should not be decrypted at all */
            numdecrypt = 0;
        }

        /* if there are bytes to decrypt, do that */
        if (numdecrypt > 0) {
            /* now decrypt the lot */
            rc = decrypt(session, &p->buf[p->readidx], p->wptr, numdecrypt);
            if (rc != PACKET_NONE) {
                return rc;
            }

            /* advance the read pointer */
            p->readidx += numdecrypt;
            /* advance write pointer */
            p->wptr += numdecrypt;
            /* increse data_num */
            p->data_num += numdecrypt;

            /* bytes left to take care of without decryption */
            numbytes -= numdecrypt;
        }

        /* if there are bytes to copy that aren't decrypted, simply
           copy them as-is to the target buffer */
        if (numbytes > 0) {
            memcpy(p->wptr, &p->buf[p->readidx], numbytes);

            /* advance the read pointer */
            p->readidx += numbytes;
            /* advance write pointer */
            p->wptr += numbytes;
            /* increse data_num */
            p->data_num += numbytes;
        }

        /* now check how much data there's left to read to finish the
           current packet */
        remainpack = p->total_num - p->data_num;

        if (!remainpack) {
            /* we have a full packet */
          libssh2_packet_read_point1:
            rc = fullpacket(session, encrypted);
            if (rc == PACKET_EAGAIN) {
                session->readPack_encrypted = encrypted;
                session->readPack_state = libssh2_NB_state_jump1;
                return PACKET_EAGAIN;
            }

            p->total_num = 0;   /* no packet buffer available */

            return rc;
        }
    } while (1);                /* loop */

    return PACKET_FAIL;         /* we never reach this point */
}
Exemple #3
0
/* {{{ libssh2_blocking_read
 * Force a blocking read, regardless of socket settings
 */
static int libssh2_blocking_read(LIBSSH2_SESSION *session, unsigned char *buf, size_t count)
{
	size_t bytes_read = 0;
#if !defined(HAVE_POLL) && !defined(HAVE_SELECT)
	int polls = 0;
#endif

#ifndef WIN32
	fcntl(session->socket_fd, F_SETFL, 0);
#else
	{
		u_long block = FALSE;
		ioctlsocket(session->socket_fd, FIONBIO, &block);
	}
#endif

#ifdef LIBSSH2_DEBUG_TRANSPORT
	_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking read: %d bytes", (int)count);
#endif

	while (bytes_read < count) {
		int ret;

		ret = recv(session->socket_fd, buf + bytes_read, count - bytes_read, LIBSSH2_SOCKET_RECV_FLAGS(session));
		if (ret < 0) {
#ifdef WIN32
			switch (WSAGetLastError()) {
				case WSAEWOULDBLOCK:	errno = EAGAIN;		break;
				case WSAENOTSOCK:		errno = EBADF;		break;
				case WSAENOTCONN:
				case WSAECONNABORTED:	errno = ENOTCONN;	break;
				case WSAEINTR:			errno = EINTR;		break;
			}
#endif
			if (errno == EAGAIN) {
#ifdef HAVE_POLL
				struct pollfd read_socket;

				read_socket.fd = session->socket_fd;
				read_socket.events = POLLIN;

				if (poll(&read_socket, 1, 30000) <= 0) {
					return -1;
				}
#elif defined(HAVE_SELECT)
				fd_set read_socket;
				struct timeval timeout;

				FD_ZERO(&read_socket);
				FD_SET(session->socket_fd, &read_socket);

				timeout.tv_sec = 30;
				timeout.tv_usec = 0;

				if (select(session->socket_fd + 1, &read_socket, NULL, NULL, &timeout) <= 0) {
					return -1;
				}
#else
				if (polls++ > LIBSSH2_SOCKET_POLL_MAXLOOPS) {
					return -1;
				}
				u_sleep(LIBSSH2_SOCKET_POLL_UDELAY);
#endif /* POLL/SELECT/SLEEP */
				continue;
			}
			if (errno == EINTR) {
				continue;
			}
			if ((errno == EBADF) || (errno == EIO) || (errno == ENOTCONN)) {
				session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
			}
			return -1;
		}
		if (ret == 0) continue;

		bytes_read += ret;
	}

#ifdef LIBSSH2_DEBUG_TRANSPORT
	_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking read: %d bytes actually read", (int)bytes_read);
#endif

	return bytes_read;
}
Exemple #4
0
/* {{{ libssh2_banner_receive
 * Wait for a hello from the remote host
 * Allocate a buffer and store the banner in session->remote.banner
 * Returns: 0 on success, PACKET_EAGAIN if read would block, 1 on failure
 */
static int
libssh2_banner_receive(LIBSSH2_SESSION * session)
{
    int ret;
    int banner_len;

    if (session->banner_TxRx_state == libssh2_NB_state_idle) {
        banner_len = 0;

        session->banner_TxRx_state = libssh2_NB_state_created;
    } else {
        banner_len = session->banner_TxRx_total_send;
    }

    while ((banner_len < (int) sizeof(session->banner_TxRx_banner)) &&
           ((banner_len == 0)
            || (session->banner_TxRx_banner[banner_len - 1] != '\n'))) {
        char c = '\0';

        ret =
            recv(session->socket_fd, &c, 1,
                 LIBSSH2_SOCKET_RECV_FLAGS(session));

        if (ret < 0) {
#ifdef WIN32
            switch (WSAGetLastError()) {
            case WSAEWOULDBLOCK:
                errno = EAGAIN;
                break;

            case WSAENOTSOCK:
                errno = EBADF;
                break;

            case WSAENOTCONN:
            case WSAECONNABORTED:
                errno = WSAENOTCONN;
                break;

            case WSAEINTR:
                errno = EINTR;
                break;
            }
#endif /* WIN32 */
            if (errno == EAGAIN) {
                session->banner_TxRx_total_send = banner_len;
                return PACKET_EAGAIN;
            }

            /* Some kinda error */
            session->banner_TxRx_state = libssh2_NB_state_idle;
            session->banner_TxRx_total_send = 0;
            return 1;
        }

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

        if (c == '\0') {
            /* NULLs are not allowed in SSH banners */
            session->banner_TxRx_state = libssh2_NB_state_idle;
            session->banner_TxRx_total_send = 0;
            return 1;
        }

        session->banner_TxRx_banner[banner_len++] = c;
    }

    while (banner_len &&
           ((session->banner_TxRx_banner[banner_len - 1] == '\n') ||
            (session->banner_TxRx_banner[banner_len - 1] == '\r'))) {
        banner_len--;
    }

    /* From this point on, we are done here */
    session->banner_TxRx_state = libssh2_NB_state_idle;
    session->banner_TxRx_total_send = 0;

    if (!banner_len)
        return 1;

    session->remote.banner = LIBSSH2_ALLOC(session, banner_len + 1);
    if (!session->remote.banner) {
        libssh2_error(session, LIBSSH2_ERROR_ALLOC,
                      "Error allocating space for remote banner", 0);
        return 1;
    }
    memcpy(session->remote.banner, session->banner_TxRx_banner, banner_len);
    session->remote.banner[banner_len] = '\0';
    _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Received Banner: %s",
                   session->remote.banner);
    return 0;
}