Пример #1
0
/* Retrieve an mp_int from the buffer.
 * Will fail for -ve since they shouldn't be required here.
 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
int buf_getmpint(buffer* buf, mp_int* mp) {

	unsigned int len;
	len = buf_getint(buf);
	
	if (len == 0) {
		mp_zero(mp);
		return DROPBEAR_SUCCESS;
	}

	if (len > BUF_MAX_MPINT) {
		return DROPBEAR_FAILURE;
	}

	/* check for negative */
	if (*buf_getptr(buf, 1) & (1 << (CHAR_BIT-1))) {
		return DROPBEAR_FAILURE;
	}

	if (mp_read_unsigned_bin(mp, buf_getptr(buf, len), len) != MP_OKAY) {
		return DROPBEAR_FAILURE;
	}

	buf_incrpos(buf, len);
	return DROPBEAR_SUCCESS;
}
Пример #2
0
/* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
static int buf_writefile(buffer * buf, const char * filename) {
	int ret = DROPBEAR_FAILURE;
	int fd = -1;

	fd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
	if (fd < 0) {
		dropbear_log(LOG_ERR, "Couldn't create new file %s: %s",
			filename, strerror(errno));
		goto out;
	}

	/* write the file now */
	while (buf->pos != buf->len) {
		int len = write(fd, buf_getptr(buf, buf->len - buf->pos),
				buf->len - buf->pos);
		if (errno == EINTR) {
			continue;
		}
		if (len <= 0) {
			dropbear_log(LOG_ERR, "Failed writing file %s: %s",
				filename, strerror(errno));
			goto out;
		}
		buf_incrpos(buf, len);
	}

	ret = DROPBEAR_SUCCESS;

out:
	if (fd >= 0) {
		m_close(fd);
	}
	return ret;
}
Пример #3
0
/* returns success or failure, and the keytype in *type. If we want
 * to restrict the type, type can contain a type to return */
int readhostkey(const char * filename, sign_key * hostkey, int *type) {

	int ret = DROPBEAR_FAILURE;
	buffer *buf;

	buf = buf_new(MAX_PRIVKEY_SIZE);

	if (buf_readfile(buf, filename) == DROPBEAR_FAILURE) {
		goto out;
	}
	buf_setpos(buf, 0);

	addrandom(buf_getptr(buf, buf->len), buf->len);

	if (buf_get_priv_key(buf, hostkey, type) == DROPBEAR_FAILURE) {
		goto out;
	}

	ret = DROPBEAR_SUCCESS;
out:

	buf_burn(buf);
	buf_free(buf);
	return ret;
}
Пример #4
0
/* Get an uint32 from the buffer and increment the pos */
unsigned int buf_getint(buffer* buf) {
	unsigned int ret;

	LOAD32H(ret, buf_getptr(buf, 4));
	buf_incrpos(buf, 4);
	return ret;
}
Пример #5
0
/* Checks the mac in hashbuf, for the data in readbuf.
 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
static int checkmac(buffer* macbuf, buffer* sourcebuf) {

	unsigned char macsize;
	hmac_state hmac;
	unsigned char tempbuf[MAX_MAC_LEN];
	unsigned long hashsize;
	int len;

	macsize = ses.keys->recv_algo_mac->hashsize;

	if (macsize == 0) {
		return DROPBEAR_SUCCESS;
	}

	/* calculate the mac */
	if (hmac_init(&hmac, 
				find_hash(ses.keys->recv_algo_mac->hashdesc->name), 
				ses.keys->recvmackey, 
				ses.keys->recv_algo_mac->keysize) 
				!= CRYPT_OK) {
		dropbear_exit("HMAC error");
	}
	
	/* sequence number */
	STORE32H(ses.recvseq, tempbuf);
	if (hmac_process(&hmac, tempbuf, 4) != CRYPT_OK) {
		dropbear_exit("HMAC error");
	}

	buf_setpos(sourcebuf, 0);
	len = sourcebuf->len;
	if (hmac_process(&hmac, buf_getptr(sourcebuf, len), len) != CRYPT_OK) {
		dropbear_exit("HMAC error");
	}

	hashsize = sizeof(tempbuf);
	if (hmac_done(&hmac, tempbuf, &hashsize) != CRYPT_OK) {
		dropbear_exit("HMAC error");
	}

	/* compare the hash */
	if (memcmp(tempbuf, buf_getptr(macbuf, macsize), macsize) != 0) {
		return DROPBEAR_FAILURE;
	} else {
		return DROPBEAR_SUCCESS;
	}
}
Пример #6
0
static void printpubkey(sign_key * key, int keytype) {

	buffer * buf = NULL;
	unsigned char base64key[MAX_PUBKEY_SIZE*2];
	unsigned long base64len;
	int err;
	const char * typestring = NULL;
	char *fp = NULL;
	int len;
	struct passwd * pw = NULL;
	char * username = NULL;
	char hostname[100];

	buf = buf_new(MAX_PUBKEY_SIZE);
	buf_put_pub_key(buf, key, keytype);
	buf_setpos(buf, 4);

	len = buf->len - buf->pos;

	base64len = sizeof(base64key);
	err = base64_encode(buf_getptr(buf, len), len, base64key, &base64len);

	if (err != CRYPT_OK) {
		fprintf(stderr, "base64 failed");
	}

	typestring = signkey_name_from_type(keytype, NULL);

	fp = sign_key_fingerprint(buf_getptr(buf, len), len);

	/* a user@host comment is informative */
	username = "";
	pw = getpwuid(getuid());
	if (pw) {
		username = pw->pw_name;
	}

	gethostname(hostname, sizeof(hostname));
	hostname[sizeof(hostname)-1] = '\0';

	printf("Public key portion is:\n%s %s %s@%s\nFingerprint: %s\n",
			typestring, base64key, username, hostname, fp);

	m_free(fp);
	buf_free(buf);
}
Пример #7
0
/* Handle a diffie-hellman key exchange reply. */
void recv_msg_kexdh_reply() {

	DEF_MP_INT(dh_f);
	sign_key *hostkey = NULL;
	unsigned int type, keybloblen;
	unsigned char* keyblob = NULL;


	TRACE(("enter recv_msg_kexdh_reply"))

	if (cli_ses.kex_state != KEXDH_INIT_SENT) {
		dropbear_exit("Received out-of-order kexdhreply");
	}
	m_mp_init(&dh_f);
	type = ses.newkeys->algo_hostkey;
	TRACE(("type is %d", type))

	hostkey = new_sign_key();
	keybloblen = buf_getint(ses.payload);

	keyblob = buf_getptr(ses.payload, keybloblen);
	if (!ses.kexstate.donefirstkex) {
		/* Only makes sense the first time */
		checkhostkey(keyblob, keybloblen);
	}

	if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) {
		TRACE(("failed getting pubkey"))
		dropbear_exit("Bad KEX packet");
	}

	if (buf_getmpint(ses.payload, &dh_f) != DROPBEAR_SUCCESS) {
		TRACE(("failed getting mpint"))
		dropbear_exit("Bad KEX packet");
	}

	kexdh_comb_key(cli_ses.dh_e, cli_ses.dh_x, &dh_f, hostkey);
	mp_clear(&dh_f);
	mp_clear_multi(cli_ses.dh_e, cli_ses.dh_x, NULL);
	m_free(cli_ses.dh_e);
	m_free(cli_ses.dh_x);

	if (buf_verify(ses.payload, hostkey, ses.hash, SHA1_HASH_SIZE) 
			!= DROPBEAR_SUCCESS) {
		dropbear_exit("Bad hostkey signature");
	}

	sign_key_free(hostkey);
	hostkey = NULL;

	send_msg_newkeys();
	ses.requirenext = SSH_MSG_NEWKEYS;
	TRACE(("leave recv_msg_kexdh_init"))
}
Пример #8
0
/* helper for svr_add_pubkey_options. returns DROPBEAR_SUCCESS if the option is matched,
   and increments the options_buf */
static int match_option(buffer *options_buf, const char *opt_name) {
    const unsigned int len = strlen(opt_name);
    if (options_buf->len - options_buf->pos < len) {
        return DROPBEAR_FAILURE;
    }
    if (strncasecmp(buf_getptr(options_buf, len), opt_name, len) == 0) {
        buf_incrpos(options_buf, len);
        return DROPBEAR_SUCCESS;
    }
    return DROPBEAR_FAILURE;
}
Пример #9
0
/* Return a string as a newly allocated buffer */
buffer * buf_getstringbuf(buffer *buf) {
	buffer *ret = NULL;
	unsigned int len = buf_getint(buf);
	if (len > MAX_STRING_LEN) {
		dropbear_exit("String too long");
	}
	ret = buf_new(len);
	memcpy(buf_getwriteptr(ret, len), buf_getptr(buf, len), len);
	buf_incrpos(buf, len);
	buf_incrlen(ret, len);
	return ret;
}
Пример #10
0
/* Executed upon receiving a kexinit message from the client to initiate
 * key exchange. If we haven't already done so, we send the list of our
 * preferred algorithms. The client's requested algorithms are processed,
 * and we calculate the first portion of the key-exchange-hash for used
 * later in the key exchange. No response is sent, as the client should
 * initiate the diffie-hellman key exchange */
void recv_msg_kexinit() {
	
	TRACE(("enter recv_msg_kexinit"));
	
	if (!ses.kexstate.sentkexinit) {
		/* we need to send a kex packet */
		send_msg_kexinit();
		TRACE(("continue recv_msg_kexinit: sent kexinit"));
	}

	/* read the client's choice of algos */
	read_kex();

	/* start the kex hash */
	ses.kexhashbuf = buf_new(MAX_KEXHASHBUF);
	/* V_C, the client's version string (CR and NL excluded) */
	buf_putstring(ses.kexhashbuf, 
			ses.remoteident, strlen((char*)ses.remoteident));
	/* V_S, the server's version string (CR and NL excluded) */
	buf_putstring(ses.kexhashbuf,
			(unsigned char*)LOCAL_IDENT, strlen(LOCAL_IDENT));

	/* I_C, the payload of the client's SSH_MSG_KEXINIT */
	buf_setpos(ses.payload, 0);
	buf_putstring(ses.kexhashbuf,
			buf_getptr(ses.payload, ses.payload->len),
			ses.payload->len);
	/* I_S, the payload of the server's SSH_MSG_KEXINIT */
	buf_putstring(ses.kexhashbuf,
			buf_getptr(ses.transkexinit, ses.transkexinit->len),
			ses.transkexinit->len);
	buf_free(ses.transkexinit);
	ses.transkexinit = NULL;
	/* the rest of ses.kexhashbuf will be done after DH exchange */

	ses.kexstate.recvkexinit = 1;
	ses.expecting = SSH_MSG_KEXDH_INIT;

	TRACE(("leave recv_msg_kexinit"));
}
Пример #11
0
/* Non-blocking function reading available portion of a packet into the
 * ses's buffer, decrypting the length if encrypted, decrypting the
 * full portion if possible */
void read_packet() {

	int len;
	unsigned int maxlen;
	unsigned char blocksize;

	TRACE(("enter read_packet"));
	blocksize = ses.keys->recv_algo_crypt->blocksize;
	
	if (ses.readbuf == NULL || ses.readbuf->len < blocksize) {
		/* In the first blocksize of a packet */

		/* Read the first blocksize of the packet, so we can decrypt it and
		 * find the length of the whole packet */
		read_packet_init();

		/* If we don't have the length of decryptreadbuf, we didn't read
		 * a whole blocksize and should exit */
		if (ses.decryptreadbuf->len == 0) {
			TRACE(("leave read_packet: packetinit done"));
			return;
		}
	}

	/* Attempt to read the remainder of the packet, note that there
	 * mightn't be any available (EAGAIN) */
	assert(ses.readbuf != NULL);
	maxlen = ses.readbuf->len - ses.readbuf->pos;
	len = read(ses.sock, buf_getptr(ses.readbuf, maxlen), maxlen);
	buf_incrpos(ses.readbuf, len);

	if (len == 0) {
		dropbear_close("remote host closed connection");
	}

	if (len < 0) {
		if (errno == EINTR || errno == EAGAIN) {
			TRACE(("leave read_packet: EINTR or EAGAIN"));
			return;
		} else {
			dropbear_exit("error reading");
		}
	}

	if ((unsigned int)len == maxlen) {
		/* The whole packet has been read */
		decrypt_packet();
		/* The main select() loop in session.h will process_packet() to
		 * handle the packet contents... */
	}
	TRACE(("leave read_packet"));
}
Пример #12
0
/* hash the ssh representation of the mp_int mp */
void sha1_process_mp(hash_state *hs, mp_int *mp) {

	int i;
	buffer * buf;

	buf = buf_new(512 + 20); /* max buffer is a 4096 bit key, 
								plus header + some leeway*/
	buf_putmpint(buf, mp);
	i = buf->pos;
	buf_setpos(buf, 0);
	sha1_process(hs, buf_getptr(buf, i), i);
	buf_free(buf);
}
Пример #13
0
char *
algolist_string(algo_type algos[])
{
	char *ret_list;
	buffer *b = buf_new(200);
	buf_put_algolist(b, algos);
	buf_setpos(b, b->len);
	buf_putbyte(b, '\0');
	buf_setpos(b, 4);
	ret_list = m_strdup((const char *) buf_getptr(b, b->len - b->pos));
	buf_free(b);
	return ret_list;
}
Пример #14
0
/* when we receive channel data, put it in a buffer for writing to the program/
 * shell etc */
void recv_msg_channel_data() {

	unsigned int chan;
	struct Channel * channel;
	unsigned int datalen;
	unsigned int pos;
	unsigned int maxdata;

	TRACE(("enter recv_msg_channel_data"));
	
	chan = buf_getint(ses.payload);
	channel = getchannel(chan);
	if (channel == NULL) {
		/* disconnect ? */
		dropbear_exit("Unknown channel");
	}

	assert(channel->infd != -1);

	datalen = buf_getint(ses.payload);

	/* if the client is going to send us more data than we've allocated, then 
	 * it has ignored the windowsize, so we "MAY ignore all extra data" */
	maxdata = channel->writebuf->size - channel->writebuf->pos;
	if (datalen > maxdata) {
		TRACE(("Warning: recv_msg_channel_data: extra data past window"));
		datalen = maxdata;
	}

	/* write to the buffer - we always append to the end of the buffer */
	pos = channel->writebuf->pos;
	buf_setpos(channel->writebuf, channel->writebuf->len);
	memcpy(buf_getwriteptr(channel->writebuf, datalen), 
			buf_getptr(ses.payload, datalen), datalen);
	buf_incrwritepos(channel->writebuf, datalen);
	buf_setpos(channel->writebuf, pos); /* revert pos */

	channel->recvwindow -= datalen;
/*	if (channel->recvwindow < RECV_MINWINDOW) {
		send_msg_channel_window_adjust(channel, 
				RECV_MAXWINDOW - channel->recvwindow);
		channel->recvwindow = RECV_MAXWINDOW;
	}*/

	TRACE(("leave recv_msg_channel_data"));
}
Пример #15
0
/* Called to write data out to the server side of a channel (eg a shell or a
 * program.
 * Only called when we know we can write to a channel, writes as much as
 * possible */
static void writechannel(struct Channel* channel) {

	int len, maxlen;
	buffer *buf;

	TRACE(("enter writechannel"));

	if (channel->recveof || channel->sentclosed) {
		TRACE(("leave writechannel: already recveof or sentclosed"));
		return;
	}

	buf = channel->writebuf;
	maxlen = buf->len - buf->pos;

	len = write(channel->infd, buf_getptr(buf, maxlen), maxlen);
	if (len <= 0) {
		if (errno != EINTR) {

			/* no more to write */
			channel->recveof = 1;

			/* if everything's closed then close it all up */
			if (channel->transeof && 
					(channel->erreof || channel->errfd == -1)) {
				send_msg_channel_close(channel);
			}
		}
		TRACE(("leave writechannel: len <= 0"));
		return;
	}
	
	/* extend the window */
	/* TODO - this is inefficient */
	if (len == maxlen) {
		buf_setpos(buf, 0);
		buf_setlen(buf, 0);
		send_msg_channel_window_adjust(channel, buf->size
				- channel->recvwindow);
		channel->recvwindow = buf->size;
	} else {
		buf_incrpos(buf, len);
	}
	TRACE(("leave writechannel"));
}
Пример #16
0
/* Create the packet mac, and append H(seqno|clearbuf) to the output */
static void writemac(buffer * outputbuffer, buffer * clearwritebuf) {

	int macsize;
	unsigned char seqbuf[4];
	unsigned long hashsize;
	hmac_state hmac;

	TRACE(("enter writemac"));

	macsize = ses.keys->trans_algo_mac->hashsize;

	if (macsize > 0) {
		/* calculate the mac */
		if (hmac_init(&hmac, 
					find_hash(ses.keys->trans_algo_mac->hashdesc->name), 
					ses.keys->transmackey, 
					ses.keys->trans_algo_mac->keysize) != CRYPT_OK) {
			dropbear_exit("HMAC error");
		}
	
		/* sequence number */
		STORE32H(ses.transseq, seqbuf);
		if (hmac_process(&hmac, seqbuf, 4) != CRYPT_OK) {
			dropbear_exit("HMAC error");
		}
	
		/* the actual contents */
		buf_setpos(clearwritebuf, 0);
		if (hmac_process(&hmac, 
					buf_getptr(clearwritebuf, 
						clearwritebuf->len),
					clearwritebuf->len) != CRYPT_OK) {
			dropbear_exit("HMAC error");
		}
	
		hashsize = macsize;
		if (hmac_done(&hmac, buf_getwriteptr(outputbuffer, macsize), &hashsize) 
				!= CRYPT_OK) {
			dropbear_exit("HMAC error");
		}
		buf_incrwritepos(outputbuffer, macsize);
	}
	TRACE(("leave writemac"));
}
Пример #17
0
/* Return a null-terminated string, it is malloced, so must be free()ed
 * Note that the string isn't checked for null bytes, hence the retlen
 * may be longer than what is returned by strlen */
char* buf_getstring(buffer* buf, unsigned int *retlen) {

	unsigned int len;
	char* ret;
	len = buf_getint(buf);
	if (len > MAX_STRING_LEN) {
		dropbear_exit("String too long");
	}

	if (retlen != NULL) {
		*retlen = len;
	}
	ret = m_malloc(len+1);
	memcpy(ret, buf_getptr(buf, len), len);
	buf_incrpos(buf, len);
	ret[len] = '\0';

	return ret;
}
Пример #18
0
/* compresses len bytes from src, outputting to dest (starting from the
 * respective current positions. */
static void buf_compress(buffer * dest, buffer * src, unsigned int len) {

	unsigned int endpos = src->pos + len;
	int result;

	TRACE(("enter buf_compress"));

	while (1) {

		ses.keys->trans_zstream->avail_in = endpos - src->pos;
		ses.keys->trans_zstream->next_in = 
			buf_getptr(src, ses.keys->trans_zstream->avail_in);

		ses.keys->trans_zstream->avail_out = dest->size - dest->pos;
		ses.keys->trans_zstream->next_out =
			buf_getwriteptr(dest, ses.keys->trans_zstream->avail_out);

		result = deflate(ses.keys->trans_zstream, Z_SYNC_FLUSH);

		buf_setpos(src, endpos - ses.keys->trans_zstream->avail_in);
		buf_setlen(dest, dest->size - ses.keys->trans_zstream->avail_out);
		buf_setpos(dest, dest->len);

		if (result != Z_OK) {
			dropbear_exit("zlib error");
		}

		if (ses.keys->trans_zstream->avail_in == 0) {
			break;
		}

		assert(ses.keys->trans_zstream->avail_out == 0);

		/* the buffer has been filled, we must extend. This only happens in
		 * unusual circumstances where the data grows in size after deflate(),
		 * but it is possible */
		buf_resize(dest, dest->size + ZLIB_COMPRESS_INCR);

	}
	TRACE(("leave buf_compress"));
}
Пример #19
0
/* non-blocking function writing out a current encrypted packet */
void write_packet() {

	int len, written;
	buffer * writebuf;
	
	TRACE(("enter write_packet"));
	assert(!isempty(&ses.writequeue));

	/* Get the next buffer in the queue of encrypted packets to write*/
	writebuf = (buffer*)examine(&ses.writequeue);

	len = writebuf->len - writebuf->pos;
	assert(len > 0);
	/* Try to write as much as possible */
	written = write(ses.sock, buf_getptr(writebuf, len), len);

	if (written < 0) {
		if (errno == EINTR) {
			TRACE(("leave writepacket: EINTR"));
			return;
		} else {
			dropbear_exit("error writing");
		}
	} 

	if (written == 0) {
		dropbear_close("remote host closed connection");
	}

	if (written == len) {
		/* We've finished with the packet, free it */
		dequeue(&ses.writequeue);
		buf_free(writebuf);
	} else {
		/* More packet left to write, leave it in the queue for later */
		buf_incrpos(writebuf, written);
	}

	TRACE(("leave write_packet"));
}
Пример #20
0
/* returns a pointer to a newly created buffer */
static buffer* buf_decompress(buffer* buf, unsigned int len) {

	int result;
	buffer * ret;
	z_streamp zstream;

	zstream = ses.keys->recv_zstream;
	ret = buf_new(len);

	zstream->avail_in = len;
	zstream->next_in = buf_getptr(buf, len);

	/* decompress the payload, incrementally resizing the output buffer */
	while (1) {

		zstream->avail_out = ret->size - ret->pos;
		zstream->next_out = buf_getwriteptr(ret, zstream->avail_out);

		result = inflate(zstream, Z_SYNC_FLUSH);

		buf_setlen(ret, ret->size - zstream->avail_out);
		buf_setpos(ret, ret->len);

		if (result != Z_BUF_ERROR && result != Z_OK) {
			dropbear_exit("zlib error");
		}

		if (zstream->avail_in == 0 &&
		   		(zstream->avail_out != 0 || result == Z_BUF_ERROR)) {
			/* we can only exit if avail_out hasn't all been used,
			 * and there's no remaining input */
			return ret;
		}

		if (zstream->avail_out == 0) {
			buf_resize(ret, ret->size + ZLIB_DECOMPRESS_INCR);
		}
	}
}
Пример #21
0
/* non-blocking function writing out a current encrypted packet */
void write_packet() {

	int len, written;
	buffer * writebuf = NULL;
	time_t now;
	unsigned packet_type;
	int all_ignore = 1;
#ifdef HAVE_WRITEV
	struct iovec *iov = NULL;
	int i;
	struct Link *l;
#endif
	
	TRACE2(("enter write_packet"))
	dropbear_assert(!isempty(&ses.writequeue));

#ifdef HAVE_WRITEV
	iov = m_malloc(sizeof(*iov) * ses.writequeue.count);
	for (l = ses.writequeue.head, i = 0; l; l = l->link, i++)
	{
		writebuf = (buffer*)l->item;
		packet_type = writebuf->data[writebuf->len-1];
		len = writebuf->len - 1 - writebuf->pos;
		dropbear_assert(len > 0);
		all_ignore &= (packet_type == SSH_MSG_IGNORE);
		TRACE2(("write_packet writev #%d  type %d len %d/%d", i, packet_type,
				len, writebuf->len-1))
		iov[i].iov_base = buf_getptr(writebuf, len);
		iov[i].iov_len = len;
	}
	written = writev(ses.sock_out, iov, ses.writequeue.count);
	if (written < 0) {
		if (errno == EINTR) {
			m_free(iov);
			TRACE2(("leave writepacket: EINTR"))
			return;
		} else {
Пример #22
0
/* Process a password auth request, sending success or failure messages as
 * appropriate */
void passwordauth() {
	
#ifdef HAVE_SHADOW_H
	struct spwd *spasswd;
#endif
	char * passwdcrypt; /* the crypt from /etc/passwd or /etc/shadow */
	char * testcrypt; /* crypt generated from the user's password sent */
	unsigned char * password;
	unsigned int passwordlen;

	unsigned char changepw;

	passwdcrypt = ses.authstate.pw->pw_passwd;
#ifdef HAVE_SHADOW_H
	/* get the shadow password if possible */
	spasswd = getspnam(ses.authstate.pw->pw_name);
	if (spasswd != NULL && spasswd->sp_pwdp != NULL) {
		passwdcrypt = spasswd->sp_pwdp;
	}
#endif

#ifdef DEBUG_HACKCRYPT
	/* debugging crypt for non-root testing with shadows */
	passwdcrypt = DEBUG_HACKCRYPT;
#endif

	/* check for empty password - need to do this again here
	 * since the shadow password may differ to that tested
	 * in auth.c */
	if (passwdcrypt[0] == '\0') {
		dropbear_log(LOG_WARNING,
				"disallowed login with empty password for '%s' from %s",
				ses.authstate.printableuser, ses.addrstring);
		send_msg_userauth_failure(0, 1);
		return;
	}

	/* check if client wants to change password */
	changepw = buf_getbyte(ses.payload);
	if (changepw) {
		/* not implemented by this server */
		send_msg_userauth_failure(0, 1);
		return;
	}

	password = buf_getstring(ses.payload, &passwordlen);

	/* clear the buffer containing the password */
	buf_incrpos(ses.payload, -passwordlen - 4);
	m_burn(buf_getptr(ses.payload, passwordlen + 4), passwordlen + 4);

	/* the first bytes of passwdcrypt are the salt */
	testcrypt = crypt((char*)password, passwdcrypt);

	if (strcmp(testcrypt, passwdcrypt) == 0) {
		/* successful authentication */
		dropbear_log(LOG_NOTICE, 
				"password auth succeeded for '%s' from %s",
				ses.authstate.printableuser, ses.addrstring);
		send_msg_userauth_success();
	} else {
		dropbear_log(LOG_WARNING,
				"bad password attempt for '%s' from %s",
				ses.authstate.printableuser, ses.addrstring);
		send_msg_userauth_failure(0, 1);
	}

	m_burn(password, passwordlen);
	m_free(password);
}
Пример #23
0
/* encrypt the writepayload, putting into writebuf, ready for write_packet()
 * to put on the wire */
void encrypt_packet() {

	unsigned char padlen;
	unsigned char blocksize, macsize;
	buffer * writebuf; /* the packet which will go on the wire */
	buffer * clearwritebuf; /* unencrypted, possibly compressed */
	
	TRACE(("enter encrypt_packet()"));
	TRACE(("encrypt_packet type is %d", ses.writepayload->data[0]));
	blocksize = ses.keys->trans_algo_crypt->blocksize;
	macsize = ses.keys->trans_algo_mac->hashsize;

	/* Encrypted packet len is payload+5, then worst case is if we are 3 away
	 * from a blocksize multiple. In which case we need to pad to the
	 * multiple, then add another blocksize (or MIN_PACKET_LEN) */
	clearwritebuf = buf_new((ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3
#ifndef DISABLE_ZLIB
			+ ZLIB_COMPRESS_INCR /* bit of a kludge, but we can't know len*/
#endif
			);
	buf_setlen(clearwritebuf, PACKET_PAYLOAD_OFF);
	buf_setpos(clearwritebuf, PACKET_PAYLOAD_OFF);

	buf_setpos(ses.writepayload, 0);

#ifndef DISABLE_ZLIB
	/* compression */
	if (ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB) {
		buf_compress(clearwritebuf, ses.writepayload, ses.writepayload->len);
	} else
#endif
	{
		memcpy(buf_getwriteptr(clearwritebuf, ses.writepayload->len),
				buf_getptr(ses.writepayload, ses.writepayload->len),
				ses.writepayload->len);
		buf_incrwritepos(clearwritebuf, ses.writepayload->len);
	}

	/* finished with payload */
	buf_setpos(ses.writepayload, 0);
	buf_setlen(ses.writepayload, 0);

	/* length of padding - packet length must be a multiple of blocksize,
	 * with a minimum of 4 bytes of padding */
	padlen = blocksize - (clearwritebuf->len) % blocksize;
	if (padlen < 4) {
		padlen += blocksize;
	}
	/* check for min packet length */
	if (clearwritebuf->len + padlen < MIN_PACKET_LEN) {
		padlen += blocksize;
	}

	buf_setpos(clearwritebuf, 0);
	/* packet length excluding the packetlength uint32 */
	buf_putint(clearwritebuf, clearwritebuf->len + padlen - 4);

	/* padding len */
	buf_putbyte(clearwritebuf, padlen);
	/* actual padding */
	buf_setpos(clearwritebuf, clearwritebuf->len);
	buf_incrlen(clearwritebuf, padlen);
	genrandom(buf_getptr(clearwritebuf, padlen), padlen);

	/* do the actual encryption */
	buf_setpos(clearwritebuf, 0);
	/* create a new writebuffer, this is freed when it has been put on the 
	 * wire by writepacket() */
	writebuf = buf_new(clearwritebuf->len + macsize);

	if (ses.keys->trans_algo_crypt->cipherdesc == NULL) {
		/* copy it */
		memcpy(buf_getwriteptr(writebuf, clearwritebuf->len),
				buf_getptr(clearwritebuf, clearwritebuf->len),
				clearwritebuf->len);
		buf_incrwritepos(writebuf, clearwritebuf->len);
	} else {
		/* encrypt it */
		while (clearwritebuf->pos < clearwritebuf->len) {
			if (cbc_encrypt(buf_getptr(clearwritebuf, blocksize),
						buf_getwriteptr(writebuf, blocksize),
						&ses.keys->trans_symmetric_struct) != CRYPT_OK) {
				dropbear_exit("error encrypting");
			}
			buf_incrpos(clearwritebuf, blocksize);
			buf_incrwritepos(writebuf, blocksize);
		}
	}

	/* now add a hmac and we're done */
	writemac(writebuf, clearwritebuf);

	/* clearwritebuf is finished with */
	buf_free(clearwritebuf);

	/* enqueue the packet for sending */
	buf_setpos(writebuf, 0);
	enqueue(&ses.writequeue, (void*)writebuf);

	/* Update counts */
	ses.kexstate.datatrans += writebuf->len;
	ses.transseq++;

	TRACE(("leave encrypt_packet()"));
}
Пример #24
0
/* handle the received packet */
void decrypt_packet() {

	unsigned char blocksize;
	unsigned char macsize;
	unsigned int padlen;
	unsigned int len;

	TRACE(("enter decrypt_packet"));
	blocksize = ses.keys->recv_algo_crypt->blocksize;
	macsize = ses.keys->recv_algo_mac->hashsize;

	ses.kexstate.datarecv += ses.readbuf->len;

	/* we've already decrypted the first blocksize in read_packet_init */
	buf_setpos(ses.readbuf, blocksize);

	buf_resize(ses.decryptreadbuf, ses.readbuf->len - macsize);
	buf_setlen(ses.decryptreadbuf, ses.decryptreadbuf->size);
	buf_setpos(ses.decryptreadbuf, blocksize);

	/* decrypt if encryption is set, memcpy otherwise */
	if (ses.keys->recv_algo_crypt->cipherdesc == NULL) {
		/* copy it */
		len = ses.readbuf->len - macsize - blocksize;
		memcpy(buf_getwriteptr(ses.decryptreadbuf, len),
				buf_getptr(ses.readbuf, len), len);
	} else {
		/* decrypt */
		while (ses.readbuf->pos < ses.readbuf->len - macsize) {
			if (cbc_decrypt(buf_getptr(ses.readbuf, blocksize), 
						buf_getwriteptr(ses.decryptreadbuf, blocksize),
						&ses.keys->recv_symmetric_struct) != CRYPT_OK) {
				dropbear_exit("error decrypting");
			}
			buf_incrpos(ses.readbuf, blocksize);
			buf_incrwritepos(ses.decryptreadbuf, blocksize);
		}
	}

	/* check the hmac */
	buf_setpos(ses.readbuf, ses.readbuf->len - macsize);
	if (checkmac(ses.readbuf, ses.decryptreadbuf) != DROPBEAR_SUCCESS) {
		dropbear_exit("Integrity error");
	}

	/* readbuf no longer required */
	buf_free(ses.readbuf);
	ses.readbuf = NULL;

	/* get padding length */
	buf_setpos(ses.decryptreadbuf, PACKET_PADDING_OFF);
	padlen = buf_getbyte(ses.decryptreadbuf);
		
	/* payload length */
	/* - 4 - 1 is for LEN and PADLEN values */
	len = ses.decryptreadbuf->len - padlen - 4 - 1;
	if ((len > MAX_PAYLOAD_LEN) || (len < 1)) {
		dropbear_exit("bad packet size");
	}

	buf_setpos(ses.decryptreadbuf, PACKET_PAYLOAD_OFF);

#ifndef DISABLE_ZLIB
	if (ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB) {
		/* decompress */
		ses.payload = buf_decompress(ses.decryptreadbuf, len);

	} else 
#endif
	{
		/* copy payload */
		ses.payload = buf_new(len);
		memcpy(ses.payload->data, buf_getptr(ses.decryptreadbuf, len), len);
		buf_incrlen(ses.payload, len);
	}

	buf_free(ses.decryptreadbuf);
	ses.decryptreadbuf = NULL;
	buf_setpos(ses.payload, 0);

	ses.recvseq++;

	TRACE(("leave decrypt_packet"));
}
Пример #25
0
/* Function used to read the initial portion of a packet, and determine the
 * length. Only called during the first BLOCKSIZE of a packet. */
static void read_packet_init() {

	unsigned int maxlen;
	int len;
	unsigned char blocksize;
	unsigned char macsize;


	blocksize = ses.keys->recv_algo_crypt->blocksize;
	macsize = ses.keys->recv_algo_mac->hashsize;

	if (ses.readbuf == NULL) {
		/* start of a new packet */
		ses.readbuf = buf_new(INIT_READBUF);
		assert(ses.decryptreadbuf == NULL);
		ses.decryptreadbuf = buf_new(blocksize);
	}

	maxlen = blocksize - ses.readbuf->pos;
			
	/* read the rest of the packet if possible */
	len = read(ses.sock, buf_getwriteptr(ses.readbuf, maxlen),
			maxlen);
	if (len == 0) {
		dropbear_close("remote host closed connection");
	}
	if (len < 0) {
		if (errno == EINTR) {
			TRACE(("leave read_packet_init: EINTR"));
			return;
		}
		dropbear_exit("error reading");
	}

	buf_incrwritepos(ses.readbuf, len);

	if ((unsigned int)len != maxlen) {
		/* don't have enough bytes to determine length, get next time */
		return;
	}

	/* now we have the first block, need to get packet length, so we decrypt
	 * the first block (only need first 4 bytes) */
	buf_setpos(ses.readbuf, 0);
	if (ses.keys->recv_algo_crypt->cipherdesc == NULL) {
		/* copy it */
		memcpy(buf_getwriteptr(ses.decryptreadbuf, blocksize),
				buf_getptr(ses.readbuf, blocksize),
				blocksize);
	} else {
		/* decrypt it */
		if (cbc_decrypt(buf_getptr(ses.readbuf, blocksize), 
					buf_getwriteptr(ses.decryptreadbuf,blocksize),
					&ses.keys->recv_symmetric_struct) != CRYPT_OK) {
			dropbear_exit("error decrypting");
		}
	}
	buf_setlen(ses.decryptreadbuf, blocksize);
	len = buf_getint(ses.decryptreadbuf) + 4 + macsize;

	buf_setpos(ses.readbuf, blocksize);

	/* check packet length */
	if ((len > MAX_PACKET_LEN) ||
		(len < MIN_PACKET_LEN + macsize) ||
		((len - macsize) % blocksize != 0)) {
		dropbear_exit("bad packet size");
	}

	buf_resize(ses.readbuf, len);
	buf_setlen(ses.readbuf, len);

}
Пример #26
0
/* Reads data from the server's program/shell/etc, and puts it in a
 * channel_data packet to send.
 * chan is the remote channel, isextended is 0 if it is normal data, 1
 * if it is extended data. if it is extended, then the type is in
 * exttype */
static void send_msg_channel_data(struct Channel *channel, int isextended,
		unsigned int exttype) {

	buffer *buf;
	int len;
	unsigned int maxlen;
	int fd;

	TRACE(("enter send_msg_channel_data"));
	TRACE(("extended = %d type = %d", isextended, exttype));

	CHECKCLEARTOWRITE();

	assert(!channel->sentclosed);

	if (isextended) {
		if (channel->erreof) {
			TRACE(("leave send_msg_channel_data: erreof already set"));
			return;
		}
		assert(exttype == SSH_EXTENDED_DATA_STDERR);
		fd = channel->errfd;
	} else {
		if (channel->transeof) {
			TRACE(("leave send_msg_channel_data: transeof already set"));
			return;
		}
		fd = channel->outfd;
	}
	assert(fd >= 0);

	maxlen = MIN(channel->transwindow, channel->transmaxpacket);
	/* -(1+4+4) is SSH_MSG_CHANNEL_DATA, channel number, string length, and 
	 * exttype if is extended */
	maxlen = MIN(maxlen, ses.writepayload->size 
			- 1 - 4 - 4 - (isextended ? 4 : 0));
	if (maxlen == 0) {
		TRACE(("leave send_msg_channel_data: no window"));
		return; /* the data will get written later */
	}

	/* read the data */
	buf = buf_new(maxlen);
	len = read(fd, buf_getwriteptr(buf, maxlen), maxlen);
	if (len <= 0) {
		/* on error etc, send eof */
		if (errno != EINTR) {
			
			if (isextended) {
				channel->erreof = 1;
			} else {
				channel->transeof = 1;
			}
			
			if ((channel->erreof || channel->errfd == -1)
					&& channel->transeof) {
				send_msg_channel_eof(channel);
			}
		}
		buf_free(buf);
		TRACE(("leave send_msg_channel_data: len <= 0, erreof %d transeof %d",
					channel->erreof, channel->transeof));
		return;
	}
	buf_incrlen(buf, len);

	buf_putbyte(ses.writepayload, 
			isextended ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA);
	buf_putint(ses.writepayload, channel->remotechan);

	if (isextended) {
		buf_putint(ses.writepayload, exttype);
	}

	buf_putstring(ses.writepayload, buf_getptr(buf, len), len);
	buf_free(buf);

	channel->transwindow -= len;

	encrypt_packet();
	TRACE(("leave send_msg_channel_data"));
}
Пример #27
0
/* Generate our side of the diffie-hellman key exchange value (dh_f), and
 * calculate the session key using the diffie-hellman algorithm. Following
 * that, the session hash is calculated, and signed with RSA or DSS. The
 * result is sent to the client. 
 *
 * See the ietf-secsh-transport draft, section 6, for details */
static void send_msg_kexdh_reply(mp_int *dh_e) {

	mp_int dh_p, dh_q, dh_g, dh_y, dh_f;
	unsigned char randbuf[DH_P_LEN];
	int dh_q_len;
	hash_state hs;

	TRACE(("enter send_msg_kexdh_reply"));
	
	assert(ses.kexstate.recvkexinit);

	m_mp_init_multi(&dh_g, &dh_p, &dh_q, &dh_y, &dh_f, NULL);

	/* read the prime and generator*/
	if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN)
			!= MP_OKAY) {
		dropbear_exit("Diffie-Hellman error");
	}
	
	if (mp_set_int(&dh_g, dh_g_val) != MP_OKAY) {
		dropbear_exit("Diffie-Hellman error");
	}

	/* calculate q = (p-1)/2 */
	if (mp_sub_d(&dh_p, 1, &dh_y) != MP_OKAY) { /*dh_y is just a temp var here*/
		dropbear_exit("Diffie-Hellman error");
	}
	if (mp_div_2(&dh_y, &dh_q) != MP_OKAY) {
		dropbear_exit("Diffie-Hellman error");
	}

	dh_q_len = mp_unsigned_bin_size(&dh_q);

	/* calculate our random value dh_y */
	do {
		assert((unsigned int)dh_q_len <= sizeof(randbuf));
		genrandom(randbuf, dh_q_len);
		if (mp_read_unsigned_bin(&dh_y, randbuf, dh_q_len) != MP_OKAY) {
			dropbear_exit("Diffie-Hellman error");
		}
	} while (mp_cmp(&dh_y, &dh_q) == MP_GT || mp_cmp_d(&dh_y, 0) != MP_GT);

	/* f = g^y mod p */
	if (mp_exptmod(&dh_g, &dh_y, &dh_p, &dh_f) != MP_OKAY) {
		dropbear_exit("Diffie-Hellman error");
	}
	mp_clear(&dh_g);

	/* K = e^y mod p */
	ses.dh_K = (mp_int*)m_malloc(sizeof(mp_int));
	m_mp_init(ses.dh_K);
	if (mp_exptmod(dh_e, &dh_y, &dh_p, ses.dh_K) != MP_OKAY) {
		dropbear_exit("Diffie-Hellman error");
	}

	/* clear no longer needed vars */
	mp_clear_multi(&dh_y, &dh_p, &dh_q, NULL);

	/* Create the remainder of the hash buffer, to generate the exchange hash */
	/* K_S, the host key */
	buf_put_pub_key(ses.kexhashbuf, ses.opts->hostkey, 
			ses.newkeys->algo_hostkey);
	/* e, exchange value sent by the client */
	buf_putmpint(ses.kexhashbuf, dh_e);
	/* f, exchange value sent by the server */
	buf_putmpint(ses.kexhashbuf, &dh_f);
	/* K, the shared secret */
	buf_putmpint(ses.kexhashbuf, ses.dh_K);

	/* calculate the hash H to sign */
	sha1_init(&hs);
	buf_setpos(ses.kexhashbuf, 0);
	sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len),
			ses.kexhashbuf->len);
	sha1_done(&hs, ses.hash);
	buf_free(ses.kexhashbuf);
	ses.kexhashbuf = NULL;
	
	/* first time around, we set the session_id to H */
	if (ses.session_id == NULL) {
		/* create the session_id, this never needs freeing */
		ses.session_id = (unsigned char*)m_malloc(SHA1_HASH_SIZE);
		memcpy(ses.session_id, ses.hash, SHA1_HASH_SIZE);
	}
	
	/* we can start creating the kexdh_reply packet */
	CHECKCLEARTOWRITE();
	buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY);
	buf_put_pub_key(ses.writepayload, ses.opts->hostkey,
			ses.newkeys->algo_hostkey);

	/* put f */
	buf_putmpint(ses.writepayload, &dh_f);
	mp_clear(&dh_f);

	/* calc the signature */
	buf_put_sign(ses.writepayload, ses.opts->hostkey, 
			ses.newkeys->algo_hostkey, ses.hash, SHA1_HASH_SIZE);

	/* the SSH_MSG_KEXDH_REPLY is done */
	encrypt_packet();

	TRACE(("leave send_msg_kexdh_reply"));
}
Пример #28
0
/* Parse pubkey options and set ses.authstate.pubkey_options accordingly.
 * Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */
int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filename) {
    int ret = DROPBEAR_FAILURE;

    TRACE(("enter addpubkeyoptions"))

    ses.authstate.pubkey_options = (struct PubKeyOptions*)m_malloc(sizeof( struct PubKeyOptions ));

    buf_setpos(options_buf, 0);
    while (options_buf->pos < options_buf->len) {
        if (match_option(options_buf, "no-port-forwarding") == DROPBEAR_SUCCESS) {
            dropbear_log(LOG_WARNING, "Port forwarding disabled.");
            ses.authstate.pubkey_options->no_port_forwarding_flag = 1;
            goto next_option;
        }
#ifdef ENABLE_SVR_AGENTFWD
        if (match_option(options_buf, "no-agent-forwarding") == DROPBEAR_SUCCESS) {
            dropbear_log(LOG_WARNING, "Agent forwarding disabled.");
            ses.authstate.pubkey_options->no_agent_forwarding_flag = 1;
            goto next_option;
        }
#endif
#ifdef ENABLE_X11FWD
        if (match_option(options_buf, "no-X11-forwarding") == DROPBEAR_SUCCESS) {
            dropbear_log(LOG_WARNING, "X11 forwarding disabled.");
            ses.authstate.pubkey_options->no_x11_forwarding_flag = 1;
            goto next_option;
        }
#endif
        if (match_option(options_buf, "no-pty") == DROPBEAR_SUCCESS) {
            dropbear_log(LOG_WARNING, "Pty allocation disabled.");
            ses.authstate.pubkey_options->no_pty_flag = 1;
            goto next_option;
        }
        if (match_option(options_buf, "command=\"") == DROPBEAR_SUCCESS) {
            int escaped = 0;
            const unsigned char* command_start = buf_getptr(options_buf, 0);
            while (options_buf->pos < options_buf->len) {
                const char c = buf_getbyte(options_buf);
                if (!escaped && c == '"') {
                    const int command_len = buf_getptr(options_buf, 0) - command_start;
                    ses.authstate.pubkey_options->forced_command = m_malloc(command_len);
                    memcpy(ses.authstate.pubkey_options->forced_command,
                           command_start, command_len-1);
                    ses.authstate.pubkey_options->forced_command[command_len-1] = '\0';
                    dropbear_log(LOG_WARNING, "Forced command '%s'",
                                 ses.authstate.pubkey_options->forced_command);
                    goto next_option;
                }
                escaped = (!escaped && c == '\\');
            }
            dropbear_log(LOG_WARNING, "Badly formatted command= authorized_keys option");
            goto bad_option;
        }

next_option:
        /*
         * Skip the comma, and move to the next option
         * (or break out if there are no more).
         */
        if (options_buf->pos < options_buf->len
                && buf_getbyte(options_buf) != ',') {
            goto bad_option;
        }
        /* Process the next option. */
    }
    /* parsed all options with no problem */
    ret = DROPBEAR_SUCCESS;
    goto end;

bad_option:
    ret = DROPBEAR_FAILURE;
    m_free(ses.authstate.pubkey_options);
    ses.authstate.pubkey_options = NULL;
    dropbear_log(LOG_WARNING, "Bad public key options at %s:%d", filename, line_num);

end:
    TRACE(("leave addpubkeyoptions"))
    return ret;
}
Пример #29
0
/* Handle a diffie-hellman key exchange reply. */
void recv_msg_kexdh_reply() {

	sign_key *hostkey = NULL;
	unsigned int type, keybloblen;
	unsigned char* keyblob = NULL;

	TRACE(("enter recv_msg_kexdh_reply"))

	if (cli_ses.kex_state != KEXDH_INIT_SENT) {
		dropbear_exit("Received out-of-order kexdhreply");
	}
	type = ses.newkeys->algo_hostkey;
	TRACE(("type is %d", type))

	hostkey = new_sign_key();
	keybloblen = buf_getint(ses.payload);

	keyblob = buf_getptr(ses.payload, keybloblen);
	if (!ses.kexstate.donefirstkex) {
		/* Only makes sense the first time */
		checkhostkey(keyblob, keybloblen);
	}

	if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) {
		TRACE(("failed getting pubkey"))
		dropbear_exit("Bad KEX packet");
	}

	switch (ses.newkeys->algo_kex->mode) {
		case DROPBEAR_KEX_NORMAL_DH:
			{
			DEF_MP_INT(dh_f);
			m_mp_init(&dh_f);
			if (buf_getmpint(ses.payload, &dh_f) != DROPBEAR_SUCCESS) {
				TRACE(("failed getting mpint"))
				dropbear_exit("Bad KEX packet");
			}

			kexdh_comb_key(cli_ses.dh_param, &dh_f, hostkey);
			mp_clear(&dh_f);
			}
			break;
		case DROPBEAR_KEX_ECDH:
#ifdef DROPBEAR_ECDH
			{
			buffer *ecdh_qs = buf_getstringbuf(ses.payload);
			kexecdh_comb_key(cli_ses.ecdh_param, ecdh_qs, hostkey);
			buf_free(ecdh_qs);
			}
#endif
			break;
#ifdef DROPBEAR_CURVE25519
		case DROPBEAR_KEX_CURVE25519:
			{
			buffer *ecdh_qs = buf_getstringbuf(ses.payload);
			kexcurve25519_comb_key(cli_ses.curve25519_param, ecdh_qs, hostkey);
			buf_free(ecdh_qs);
			}
#endif
			break;
	}

	if (cli_ses.dh_param) {
		free_kexdh_param(cli_ses.dh_param);
		cli_ses.dh_param = NULL;
	}
#ifdef DROPBEAR_ECDH
	if (cli_ses.ecdh_param) {
		free_kexecdh_param(cli_ses.ecdh_param);
		cli_ses.ecdh_param = NULL;
	}
#endif
#ifdef DROPBEAR_CURVE25519
	if (cli_ses.curve25519_param) {
		free_kexcurve25519_param(cli_ses.curve25519_param);
		cli_ses.curve25519_param = NULL;
	}
#endif

	cli_ses.param_kex_algo = NULL;
	if (buf_verify(ses.payload, hostkey, ses.hash) != DROPBEAR_SUCCESS) {
		dropbear_exit("Bad hostkey signature");
	}

	sign_key_free(hostkey);
	hostkey = NULL;

	send_msg_newkeys();
	ses.requirenext[0] = SSH_MSG_NEWKEYS;
	ses.requirenext[1] = 0;
	TRACE(("leave recv_msg_kexdh_init"))
}