Commit message: None
CVE: CVE-2016-1907
CWE: CWE-119
Vulnerability description: The ssh_packet_read_poll2 function in packet.c in OpenSSH before 7.1p2 allows remote attackers to cause a denial of service (out-of-bounds read and application crash) via crafted network traffic.

Project: mindrot

compress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out)
{
	u_char buf[4096];
	int r, status;

	if (ssh->state->compression_out_started != 1)
		return SSH_ERR_INTERNAL_ERROR;

	/* This case is not handled below. */
	if (sshbuf_len(in) == 0)
		return 0;

	/* Input is the contents of the input buffer. */
	if ((ssh->state->compression_out_stream.next_in =
	    sshbuf_mutable_ptr(in)) == NULL)
		return SSH_ERR_INTERNAL_ERROR;
	ssh->state->compression_out_stream.avail_in = sshbuf_len(in);

	/* Loop compressing until deflate() returns with avail_out != 0. */
	do {
		/* Set up fixed-size output buffer. */
		ssh->state->compression_out_stream.next_out = buf;
		ssh->state->compression_out_stream.avail_out = sizeof(buf);

		/* Compress as much data into the buffer as possible. */
		status = deflate(&ssh->state->compression_out_stream,
		    Z_PARTIAL_FLUSH);
		switch (status) {
		case Z_MEM_ERROR:
			return SSH_ERR_ALLOC_FAIL;
		case Z_OK:
			/* Append compressed data to output_buffer. */
			if ((r = sshbuf_put(out, buf, sizeof(buf) -
			    ssh->state->compression_out_stream.avail_out)) != 0)
				return r;
			break;
		case Z_STREAM_ERROR:
		default:
			ssh->state->compression_out_failures++;
			return SSH_ERR_INVALID_FORMAT;
		}
	} while (ssh->state->compression_out_stream.avail_out == 0);
	return 0;
}

Project: mindrot

newkeys_to_blob(struct sshbuf *m, struct ssh *ssh, int mode)
{
	struct sshbuf *b;
	struct sshcipher_ctx *cc;
	struct sshcomp *comp;
	struct sshenc *enc;
	struct sshmac *mac;
	struct newkeys *newkey;
	int r;

	if ((newkey = ssh->state->newkeys[mode]) == NULL)
		return SSH_ERR_INTERNAL_ERROR;
	enc = &newkey->enc;
	mac = &newkey->mac;
	comp = &newkey->comp;
	cc = (mode == MODE_OUT) ? &ssh->state->send_context :
	    &ssh->state->receive_context;
	if ((r = cipher_get_keyiv(cc, enc->iv, enc->iv_len)) != 0)
		return r;
	if ((b = sshbuf_new()) == NULL)
		return SSH_ERR_ALLOC_FAIL;
	/* The cipher struct is constant and shared, you export pointer */
	if ((r = sshbuf_put_cstring(b, enc->name)) != 0 ||
	    (r = sshbuf_put(b, &enc->cipher, sizeof(enc->cipher))) != 0 ||
	    (r = sshbuf_put_u32(b, enc->enabled)) != 0 ||
	    (r = sshbuf_put_u32(b, enc->block_size)) != 0 ||
	    (r = sshbuf_put_string(b, enc->key, enc->key_len)) != 0 ||
	    (r = sshbuf_put_string(b, enc->iv, enc->iv_len)) != 0)
		goto out;
	if (cipher_authlen(enc->cipher) == 0) {
		if ((r = sshbuf_put_cstring(b, mac->name)) != 0 ||
		    (r = sshbuf_put_u32(b, mac->enabled)) != 0 ||
		    (r = sshbuf_put_string(b, mac->key, mac->key_len)) != 0)
			goto out;
	}
	if ((r = sshbuf_put_u32(b, comp->type)) != 0 ||
	    (r = sshbuf_put_u32(b, comp->enabled)) != 0 ||
	    (r = sshbuf_put_cstring(b, comp->name)) != 0)
		goto out;
	r = sshbuf_put_stringb(m, b);
 out:
	if (b != NULL)
		sshbuf_free(b);
	return r;
}

Project: mindrot

ssh_packet_not_very_much_data_to_write(struct ssh *ssh)
{
	if (ssh->state->interactive_mode)
		return sshbuf_len(ssh->state->output) < 16384;
	else
		return sshbuf_len(ssh->state->output) < 128 * 1024;
}

Project: mindrot

ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
{
	struct session_state *state = ssh->state;
	u_int reason, seqnr;
	int r;
	u_char *msg;

	for (;;) {
		msg = NULL;
		if (compat20) {
			r = ssh_packet_read_poll2(ssh, typep, seqnr_p);
			if (r != 0)
				return r;
			if (*typep) {
				state->keep_alive_timeouts = 0;
				DBG(debug("received packet type %d", *typep));
			}
			switch (*typep) {
			case SSH2_MSG_IGNORE:
				debug3("Received SSH2_MSG_IGNORE");
				break;
			case SSH2_MSG_DEBUG:
				if ((r = sshpkt_get_u8(ssh, NULL)) != 0 ||
				    (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
				    (r = sshpkt_get_string(ssh, NULL, NULL)) != 0) {
					if (msg)
						free(msg);
					return r;
				}
				debug("Remote: %.900s", msg);
				free(msg);
				break;
			case SSH2_MSG_DISCONNECT:
				if ((r = sshpkt_get_u32(ssh, &reason)) != 0 ||
				    (r = sshpkt_get_string(ssh, &msg, NULL)) != 0)
					return r;
				/* Ignore normal client exit notifications */
				do_log2(ssh->state->server_side &&
				    reason == SSH2_DISCONNECT_BY_APPLICATION ?
				    SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_ERROR,
				    "Received disconnect from %s: %u: %.400s",
				    ssh_remote_ipaddr(ssh), reason, msg);
				free(msg);
				return SSH_ERR_DISCONNECTED;
			case SSH2_MSG_UNIMPLEMENTED:
				if ((r = sshpkt_get_u32(ssh, &seqnr)) != 0)
					return r;
				debug("Received SSH2_MSG_UNIMPLEMENTED for %u",
				    seqnr);
				break;
			default:
				return 0;
			}
		} else {
			r = ssh_packet_read_poll1(ssh, typep);
			switch (*typep) {
			case SSH_MSG_NONE:
				return SSH_MSG_NONE;
			case SSH_MSG_IGNORE:
				break;
			case SSH_MSG_DEBUG:
				if ((r = sshpkt_get_string(ssh, &msg, NULL)) != 0)
					return r;
				debug("Remote: %.900s", msg);
				free(msg);
				break;
			case SSH_MSG_DISCONNECT:
				if ((r = sshpkt_get_string(ssh, &msg, NULL)) != 0)
					return r;
				error("Received disconnect from %s: %.400s",
				    ssh_remote_ipaddr(ssh), msg);
				free(msg);
				return SSH_ERR_DISCONNECTED;
			default:
				DBG(debug("received packet type %d", *typep));
				return 0;
			}
		}
	}
}

Project: mindrot

ssh_packet_send2(struct ssh *ssh)
{
	struct session_state *state = ssh->state;
	struct packet *p;
	u_char type;
	int r;

	type = sshbuf_ptr(state->outgoing_packet)[5];

	/* during rekeying we can only send key exchange messages */
	if (state->rekeying) {
		if ((type < SSH2_MSG_TRANSPORT_MIN) ||
		    (type > SSH2_MSG_TRANSPORT_MAX) ||
		    (type == SSH2_MSG_SERVICE_REQUEST) ||
		    (type == SSH2_MSG_SERVICE_ACCEPT)) {
			debug("enqueue packet: %u", type);
			p = calloc(1, sizeof(*p));
			if (p == NULL)
				return SSH_ERR_ALLOC_FAIL;
			p->type = type;
			p->payload = state->outgoing_packet;
			TAILQ_INSERT_TAIL(&state->outgoing, p, next);
			state->outgoing_packet = sshbuf_new();
			if (state->outgoing_packet == NULL)
				return SSH_ERR_ALLOC_FAIL;
			return 0;
		}
	}

	/* rekeying starts with sending KEXINIT */
	if (type == SSH2_MSG_KEXINIT)
		state->rekeying = 1;

	if ((r = ssh_packet_send2_wrapped(ssh)) != 0)
		return r;

	/* after a NEWKEYS message we can send the complete queue */
	if (type == SSH2_MSG_NEWKEYS) {
		state->rekeying = 0;
		state->rekey_time = monotime();
		while ((p = TAILQ_FIRST(&state->outgoing))) {
			type = p->type;
			debug("dequeue packet: %u", type);
			sshbuf_free(state->outgoing_packet);
			state->outgoing_packet = p->payload;
			TAILQ_REMOVE(&state->outgoing, p, next);
			free(p);
			if ((r = ssh_packet_send2_wrapped(ssh)) != 0)
				return r;
		}
	}
	return 0;
}

Project: mindrot

ssh_packet_set_protocol_flags(struct ssh *ssh, u_int protocol_flags)
{
	ssh->state->remote_protocol_flags = protocol_flags;
}

Project: mindrot

ssh_packet_set_rekey_limits(struct ssh *ssh, u_int32_t bytes, time_t seconds)
{
	debug3("rekey after %lld bytes, %d seconds", (long long)bytes,
	    (int)seconds);
	ssh->state->rekey_limit = bytes;
	ssh->state->rekey_interval = seconds;
}

Project: mindrot

sshpkt_add_padding(struct ssh *ssh, u_char pad)
{
	ssh->state->extra_pad = pad;
	return 0;
}

Project: mindrot

sshpkt_get_bignum1(struct ssh *ssh, BIGNUM *v)
{
	return sshbuf_get_bignum1(ssh->state->incoming_packet, v);
}

Project: mindrot

sshpkt_get_end(struct ssh *ssh)
{
	if (sshbuf_len(ssh->state->incoming_packet) > 0)
		return SSH_ERR_UNEXPECTED_TRAILING_DATA;
	return 0;
}

Project: mindrot

sshpkt_get_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp)
{
	return sshbuf_get_string_direct(ssh->state->incoming_packet, valp, lenp);
}