changeset 1061:a64ef3c2b371

merge
author Matt Johnston <matt@ucc.asn.au>
date Sun, 01 Mar 2015 23:27:08 +0800
parents 4c733310c21d (diff) 063c38ea622b (current diff)
children 210982935887 9a5677293671
files
diffstat 14 files changed, 174 insertions(+), 101 deletions(-) [+]
line wrap: on
line diff
--- a/buffer.c	Sun Mar 01 22:44:36 2015 +0800
+++ b/buffer.c	Sun Mar 01 23:27:08 2015 +0800
@@ -46,17 +46,15 @@
 		dropbear_exit("buf->size too big");
 	}
 
-	buf = (buffer*)m_malloc(sizeof(buffer));
+	buf = (buffer*)m_malloc(sizeof(buffer)+size);
 
 	if (size > 0) {
-		buf->data = (unsigned char*)m_malloc(size);
+		buf->data = (unsigned char*)buf + sizeof(buffer);
 	} else {
 		buf->data = NULL;
 	}
 
 	buf->size = size;
-	buf->pos = 0;
-	buf->len = 0;
 
 	return buf;
 
@@ -65,7 +63,6 @@
 /* free the buffer's data and the buffer itself */
 void buf_free(buffer* buf) {
 
-	m_free(buf->data)
 	m_free(buf);
 }
 
@@ -78,17 +75,18 @@
 
 /* resize a buffer, pos and len will be repositioned if required when
  * downsizing */
-void buf_resize(buffer *buf, unsigned int newsize) {
+buffer* buf_resize(buffer *buf, unsigned int newsize) {
 
 	if (newsize > BUF_MAX_SIZE) {
 		dropbear_exit("buf->size too big");
 	}
 
-	buf->data = m_realloc(buf->data, newsize);
+	buf = m_realloc(buf, sizeof(buffer)+newsize);
+	buf->data = (unsigned char*)buf + sizeof(buffer);
 	buf->size = newsize;
 	buf->len = MIN(newsize, buf->len);
 	buf->pos = MIN(newsize, buf->pos);
-
+	return buf;
 }
 
 /* Create a copy of buf, allocating required memory etc. */
@@ -227,15 +225,15 @@
 
 /* Return a string as a newly allocated buffer */
 buffer * buf_getstringbuf(buffer *buf) {
-	buffer *ret;
-	unsigned char* str;
-	unsigned int len;
-	str = buf_getstring(buf, &len);
-	ret = m_malloc(sizeof(*ret));
-	ret->data = str;
-	ret->len = len;
-	ret->size = len;
-	ret->pos = 0;
+	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;
 }
 
--- a/buffer.h	Sun Mar 01 22:44:36 2015 +0800
+++ b/buffer.h	Sun Mar 01 23:27:08 2015 +0800
@@ -29,7 +29,8 @@
 #include "includes.h"
 
 struct buf {
-
+	/* don't manipulate data member outside of buffer.c - it
+	is a pointer into the malloc holding buffer itself */
 	unsigned char * data;
 	unsigned int len; /* the used size */
 	unsigned int pos;
@@ -40,7 +41,8 @@
 typedef struct buf buffer;
 
 buffer * buf_new(unsigned int size);
-void buf_resize(buffer *buf, unsigned int newsize);
+/* Possibly returns a new buffer*, like realloc() */
+buffer * buf_resize(buffer *buf, unsigned int newsize);
 void buf_free(buffer* buf);
 void buf_burn(buffer* buf);
 buffer* buf_newcopy(buffer* buf);
--- a/circbuffer.c	Sun Mar 01 22:44:36 2015 +0800
+++ b/circbuffer.c	Sun Mar 01 23:27:08 2015 +0800
@@ -110,6 +110,21 @@
 	return &cbuf->data[cbuf->readpos];
 }
 
+void cbuf_readptrs(circbuffer *cbuf, 
+	unsigned char **p1, unsigned int *len1, 
+	unsigned char **p2, unsigned int *len2) {
+	*p1 = &cbuf->data[cbuf->readpos];
+	*len1 = MIN(cbuf->used, cbuf->size - cbuf->readpos);
+
+	if (*len1 < cbuf->used) {
+		*p2 = cbuf->data;
+		*len2 = cbuf->used - *len1;
+	} else {
+		*p2 = NULL;
+		*len2 = 0;
+	}
+}
+
 unsigned char* cbuf_writeptr(circbuffer *cbuf, unsigned int len) {
 
 	if (len > cbuf_writelen(cbuf)) {
@@ -131,9 +146,11 @@
 
 
 void cbuf_incrread(circbuffer *cbuf, unsigned int len) {
+#if 0
 	if (len > cbuf_readlen(cbuf)) {
 		dropbear_exit("Bad cbuf read");
 	}
+#endif
 
 	dropbear_assert(cbuf->used >= len);
 	cbuf->used -= len;
--- a/circbuffer.h	Sun Mar 01 22:44:36 2015 +0800
+++ b/circbuffer.h	Sun Mar 01 23:27:08 2015 +0800
@@ -44,6 +44,9 @@
 unsigned int cbuf_writelen(circbuffer *cbuf); /* max linear write len */
 
 unsigned char* cbuf_readptr(circbuffer *cbuf, unsigned int len);
+void cbuf_readptrs(circbuffer *cbuf, 
+	unsigned char **p1, unsigned int *len1, 
+	unsigned char **p2, unsigned int *len2);
 unsigned char* cbuf_writeptr(circbuffer *cbuf, unsigned int len);
 void cbuf_incrwrite(circbuffer *cbuf, unsigned int len);
 void cbuf_incrread(circbuffer *cbuf, unsigned int len);
--- a/cli-agentfwd.c	Sun Mar 01 22:44:36 2015 +0800
+++ b/cli-agentfwd.c	Sun Mar 01 23:27:08 2015 +0800
@@ -155,7 +155,7 @@
 		goto out;
 	}
 	
-	buf_resize(inbuf, readlen);
+	inbuf = buf_resize(inbuf, readlen);
 	buf_setpos(inbuf, 0);
 	ret = atomicio(read, fd, buf_getwriteptr(inbuf, readlen), readlen);
 	if ((size_t)ret != readlen) {
--- a/common-channel.c	Sun Mar 01 22:44:36 2015 +0800
+++ b/common-channel.c	Sun Mar 01 23:27:08 2015 +0800
@@ -42,7 +42,8 @@
 static void send_msg_channel_open_confirmation(struct Channel* channel,
 		unsigned int recvwindow, 
 		unsigned int recvmaxpacket);
-static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf);
+static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf,
+	const unsigned char *moredata, unsigned int *morelen);
 static void send_msg_channel_window_adjust(struct Channel *channel, 
 		unsigned int incr);
 static void send_msg_channel_data(struct Channel *channel, int isextended);
@@ -241,14 +242,14 @@
 
 		/* write to program/pipe stdin */
 		if (channel->writefd >= 0 && FD_ISSET(channel->writefd, writefds)) {
-			writechannel(channel, channel->writefd, channel->writebuf);
+			writechannel(channel, channel->writefd, channel->writebuf, NULL, NULL);
 			do_check_close = 1;
 		}
 		
 		/* stderr for client mode */
 		if (ERRFD_IS_WRITE(channel)
 				&& channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) {
-			writechannel(channel, channel->errfd, channel->extrabuf);
+			writechannel(channel, channel->errfd, channel->extrabuf, NULL, NULL);
 			do_check_close = 1;
 		}
 
@@ -434,14 +435,67 @@
 }
 
 /* Called to write data out to the local side of the channel. 
- * Only called when we know we can write to a channel, writes as much as
- * possible */
-static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf) {
+   Writes the circular buffer contents and also the "moredata" buffer
+   if not null. Will ignore EAGAIN */
+static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf,
+	const unsigned char *moredata, unsigned int *morelen) {
 
-	int len, maxlen;
+	struct iovec iov[3];
+	unsigned char *circ_p1, *circ_p2;
+	unsigned int circ_len1, circ_len2;
+	int io_count = 0;
+
+	int written;
 
 	TRACE(("enter writechannel fd %d", fd))
 
+	cbuf_readptrs(cbuf, &circ_p1, &circ_len1, &circ_p2, &circ_len2);
+
+	if (circ_len1 > 0) {
+		TRACE(("circ1 %d", circ_len1))
+		iov[io_count].iov_base = circ_p1;
+		iov[io_count].iov_len = circ_len1;
+		io_count++;
+	}
+
+	if (circ_len2 > 0) {
+		TRACE(("circ2 %d", circ_len2))
+		iov[io_count].iov_base = circ_p2;
+		iov[io_count].iov_len = circ_len2;
+		io_count++;
+	}
+
+	if (morelen) {
+		assert(moredata);
+		TRACE(("more %d", *morelen))
+		iov[io_count].iov_base = (void*)moredata;
+		iov[io_count].iov_len  = *morelen;
+		io_count++;
+	}
+
+	if (morelen) {
+		/* Default return value, none consumed */
+		*morelen = 0;
+	}
+
+	written = writev(fd, iov, io_count);
+
+	if (written < 0) {
+		if (errno != EINTR && errno != EAGAIN) {
+			TRACE(("errno %d len %d", errno, len))
+			close_chan_fd(channel, fd, SHUT_WR);
+		}
+	} else {
+		int cbuf_written = MIN(circ_len1+circ_len2, (unsigned int)written);
+		cbuf_incrread(cbuf, cbuf_written);
+		if (morelen) {
+			*morelen = written - cbuf_written;
+		}
+		channel->recvdonelen += written;
+	}
+
+#if 0
+
 	maxlen = cbuf_readlen(cbuf);
 
 	/* Write the data out */
@@ -458,10 +512,10 @@
 
 	cbuf_incrread(cbuf, len);
 	channel->recvdonelen += len;
+#endif
 
 	/* Window adjust handling */
 	if (channel->recvdonelen >= RECV_WINDOWEXTEND) {
-		/* Set it back to max window */
 		send_msg_channel_window_adjust(channel, channel->recvdonelen);
 		channel->recvwindow += channel->recvdonelen;
 		channel->recvdonelen = 0;
@@ -745,6 +799,7 @@
 	unsigned int maxdata;
 	unsigned int buflen;
 	unsigned int len;
+	unsigned int consumed;
 
 	TRACE(("enter recv_msg_channel_data"))
 
@@ -771,6 +826,17 @@
 		dropbear_exit("Oversized packet");
 	}
 
+	dropbear_assert(channel->recvwindow >= datalen);
+	channel->recvwindow -= datalen;
+	dropbear_assert(channel->recvwindow <= opts.recv_window);
+
+	consumed = datalen;
+	writechannel(channel, fd, cbuf, buf_getptr(ses.payload, datalen), &consumed);
+
+	datalen -= consumed;
+	buf_incrpos(ses.payload, consumed);
+
+
 	/* We may have to run throught twice, if the buffer wraps around. Can't
 	 * just "leave it for next time" like with writechannel, since this
 	 * is payload data */
@@ -786,10 +852,6 @@
 		len -= buflen;
 	}
 
-	dropbear_assert(channel->recvwindow >= datalen);
-	channel->recvwindow -= datalen;
-	dropbear_assert(channel->recvwindow <= opts.recv_window);
-
 	TRACE(("leave recv_msg_channel_data"))
 }
 
--- a/common-kex.c	Sun Mar 01 22:44:36 2015 +0800
+++ b/common-kex.c	Sun Mar 01 23:27:08 2015 +0800
@@ -534,8 +534,10 @@
 	    buf_putstring(ses.kexhashbuf,
 			ses.transkexinit->data, ses.transkexinit->len);
 		/* I_S, the payload of the server's SSH_MSG_KEXINIT */
-	    buf_setpos(ses.payload, 0);
-	    buf_putstring(ses.kexhashbuf, ses.payload->data, ses.payload->len);
+	    buf_setpos(ses.payload, ses.payload_beginning);
+	    buf_putstring(ses.kexhashbuf, 
+	    	buf_getptr(ses.payload, ses.payload->len-ses.payload->pos),
+	    	ses.payload->len-ses.payload->pos);
 		ses.requirenext = SSH_MSG_KEXDH_REPLY;
 	} else {
 		/* SERVER */
@@ -549,8 +551,10 @@
 				(unsigned char*)LOCAL_IDENT, local_ident_len);
 
 		/* I_C, the payload of the client's SSH_MSG_KEXINIT */
-	    buf_setpos(ses.payload, 0);
-	    buf_putstring(ses.kexhashbuf, ses.payload->data, ses.payload->len);
+	    buf_setpos(ses.payload, ses.payload_beginning);
+	    buf_putstring(ses.kexhashbuf, 
+	    	buf_getptr(ses.payload, ses.payload->len-ses.payload->pos),
+	    	ses.payload->len-ses.payload->pos);
 
 		/* I_S, the payload of the server's SSH_MSG_KEXINIT */
 	    buf_putstring(ses.kexhashbuf,
--- a/common-session.c	Sun Mar 01 22:44:36 2015 +0800
+++ b/common-session.c	Sun Mar 01 23:27:08 2015 +0800
@@ -152,8 +152,10 @@
 		FD_ZERO(&readfd);
 		dropbear_assert(ses.payload == NULL);
 
-		/* during initial setup we flush out the KEXINIT packet before
-		 * attempting to read the remote version string, which might block */
+		/* We delay reading from the input socket during initial setup until
+		after we have written out our initial KEXINIT packet (empty writequeue). 
+		This means our initial packet can be in-flight while we're doing a blocking
+		read for the remote ident */
 		if (ses.sock_in != -1 && (ses.remoteident || isempty(&ses.writequeue))) {
 			FD_SET(ses.sock_in, &readfd);
 		}
--- a/libtomcrypt/src/mac/hmac/hmac_done.c	Sun Mar 01 22:44:36 2015 +0800
+++ b/libtomcrypt/src/mac/hmac/hmac_done.c	Sun Mar 01 23:27:08 2015 +0800
@@ -28,7 +28,7 @@
 */
 int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen)
 {
-    unsigned char *buf, *isha;
+    unsigned char buf[MAXBLOCKSIZE], isha[MAXBLOCKSIZE];
     unsigned long hashsize, i;
     int hash, err;
 
@@ -44,19 +44,6 @@
     /* get the hash message digest size */
     hashsize = hash_descriptor[hash].hashsize;
 
-    /* allocate buffers */
-    buf  = XMALLOC(HMAC_BLOCKSIZE);
-    isha = XMALLOC(hashsize);
-    if (buf == NULL || isha == NULL) { 
-       if (buf != NULL) {
-          XFREE(buf);
-       } 
-       if (isha != NULL) {
-          XFREE(isha);
-       }  
-       return CRYPT_MEM;
-    }
-
     /* Get the hash of the first HMAC vector plus the data */
     if ((err = hash_descriptor[hash].done(&hmac->md, isha)) != CRYPT_OK) {
        goto LBL_ERR;
@@ -96,9 +83,6 @@
     zeromem(hmac, sizeof(*hmac));
 #endif
 
-    XFREE(isha);
-    XFREE(buf);
-
     return err;
 }
 
--- a/libtomcrypt/src/mac/hmac/hmac_init.c	Sun Mar 01 22:44:36 2015 +0800
+++ b/libtomcrypt/src/mac/hmac/hmac_init.c	Sun Mar 01 23:27:08 2015 +0800
@@ -29,7 +29,7 @@
 */
 int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned long keylen)
 {
-    unsigned char *buf;
+    unsigned char buf[MAXBLOCKSIZE];
     unsigned long hashsize;
     unsigned long i, z;
     int err;
@@ -49,16 +49,9 @@
         return CRYPT_INVALID_KEYSIZE;
     }
 
-    /* allocate ram for buf */
-    buf = XMALLOC(HMAC_BLOCKSIZE);
-    if (buf == NULL) {
-       return CRYPT_MEM;
-    }
-
     /* allocate memory for key */
     hmac->key = XMALLOC(HMAC_BLOCKSIZE);
     if (hmac->key == NULL) {
-       XFREE(buf);
        return CRYPT_MEM;
     }
 
@@ -101,7 +94,6 @@
    zeromem(buf, HMAC_BLOCKSIZE);
 #endif
  
-   XFREE(buf);
    return err;    
 }
 
--- a/netio.h	Sun Mar 01 22:44:36 2015 +0800
+++ b/netio.h	Sun Mar 01 23:27:08 2015 +0800
@@ -27,6 +27,7 @@
 errstring is only set on DROPBEAR_FAILURE, returns failure message for the last attempted socket */
 typedef void(*connect_callback)(int result, int sock, void* data, const char* errstring);
 
+/* Always returns a progress connection, if it fails it will call the callback at a later point */
 struct dropbear_progress_connection * connect_remote (const char* remotehost, const char* remoteport,
 	connect_callback cb, void *cb_data);
 
--- a/packet.c	Sun Mar 01 22:44:36 2015 +0800
+++ b/packet.c	Sun Mar 01 23:27:08 2015 +0800
@@ -257,7 +257,7 @@
 	}
 
 	if (len > ses.readbuf->size) {
-		buf_resize(ses.readbuf, len);		
+		ses.readbuf = buf_resize(ses.readbuf, len);		
 	}
 	buf_setlen(ses.readbuf, len);
 	buf_setpos(ses.readbuf, blocksize);
@@ -314,18 +314,21 @@
 	if (is_compress_recv()) {
 		/* decompress */
 		ses.payload = buf_decompress(ses.readbuf, len);
+		buf_setpos(ses.payload, 0);
+		ses.payload_beginning = 0;
+		buf_free(ses.readbuf);
 	} else 
 #endif
 	{
+		ses.payload = ses.readbuf;
+		ses.payload_beginning = ses.payload->pos;
+		buf_setlen(ses.payload, ses.payload->pos + len);
 		/* copy payload */
-		ses.payload = buf_new(len);
-		memcpy(ses.payload->data, buf_getptr(ses.readbuf, len), len);
-		buf_incrlen(ses.payload, len);
+		//ses.payload = buf_new(len);
+		//memcpy(ses.payload->data, buf_getptr(ses.readbuf, len), len);
+		//buf_incrlen(ses.payload, len);
 	}
-
-	buf_free(ses.readbuf);
 	ses.readbuf = NULL;
-	buf_setpos(ses.payload, 0);
 
 	ses.recvseq++;
 
@@ -398,7 +401,7 @@
 				dropbear_exit("bad packet, oversized decompressed");
 			}
 			new_size = MIN(RECV_MAX_PAYLOAD_LEN, ret->size + ZLIB_DECOMPRESS_INCR);
-			buf_resize(ret, new_size);
+			ret = buf_resize(ret, new_size);
 		}
 	}
 }
@@ -637,7 +640,8 @@
 
 #ifndef DISABLE_ZLIB
 /* compresses len bytes from src, outputting to dest (starting from the
- * respective current positions. */
+ * respective current positions. dest must have sufficient space,
+ * len+ZLIB_COMPRESS_EXPANSION */
 static void buf_compress(buffer * dest, buffer * src, unsigned int len) {
 
 	unsigned int endpos = src->pos + len;
@@ -645,38 +649,28 @@
 
 	TRACE2(("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);
+	dropbear_assert(dest->size - dest->pos >= len+ZLIB_COMPRESS_EXPANSION);
 
-		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);
+	ses.keys->trans.zstream->avail_in = endpos - src->pos;
+	ses.keys->trans.zstream->next_in = 
+		buf_getptr(src, ses.keys->trans.zstream->avail_in);
 
-		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);
+	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);
 
-		if (result != Z_OK) {
-			dropbear_exit("zlib error");
-		}
+	result = deflate(ses.keys->trans.zstream, Z_SYNC_FLUSH);
 
-		if (ses.keys->trans.zstream->avail_in == 0) {
-			break;
-		}
+	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);
 
-		dropbear_assert(ses.keys->trans.zstream->avail_out == 0);
+	if (result != Z_OK) {
+		dropbear_exit("zlib error");
+	}
 
-		/* 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_EXPANSION);
-
-	}
+	/* fails if destination buffer wasn't large enough */
+	dropbear_assert(ses.keys->trans.zstream->avail_in == 0);
 	TRACE2(("leave buf_compress"))
 }
 #endif
--- a/session.h	Sun Mar 01 22:44:36 2015 +0800
+++ b/session.h	Sun Mar 01 23:27:08 2015 +0800
@@ -126,7 +126,11 @@
 							 buffer with the packet to send. */
 	struct Queue writequeue; /* A queue of encrypted packets to send */
 	buffer *readbuf; /* From the wire, decrypted in-place */
-	buffer *payload; /* Post-decompression, the actual SSH packet */
+	buffer *payload; /* Post-decompression, the actual SSH packet. 
+						May have extra data at the beginning, will be
+						passed to packet processing functions positioned past
+						that, see payload_beginning */
+	unsigned int payload_beginning;
 	unsigned int transseq, recvseq; /* Sequence IDs */
 
 	/* Packet-handling flags */
--- a/svr-authpubkey.c	Sun Mar 01 22:44:36 2015 +0800
+++ b/svr-authpubkey.c	Sun Mar 01 23:27:08 2015 +0800
@@ -86,6 +86,7 @@
 	unsigned int algolen;
 	unsigned char* keyblob = NULL;
 	unsigned int keybloblen;
+	unsigned int sign_payload_length;
 	buffer * signbuf = NULL;
 	sign_key * key = NULL;
 	char* fp = NULL;
@@ -125,9 +126,18 @@
 
 	/* create the data which has been signed - this a string containing
 	 * session_id, concatenated with the payload packet up to the signature */
+	assert(ses.payload_beginning <= ses.payload->pos);
+	sign_payload_length = ses.payload->pos - ses.payload_beginning;
 	signbuf = buf_new(ses.payload->pos + 4 + ses.session_id->len);
 	buf_putbufstring(signbuf, ses.session_id);
-	buf_putbytes(signbuf, ses.payload->data, ses.payload->pos);
+
+	/* The entire contents of the payload prior. */
+	buf_setpos(ses.payload, ses.payload_beginning);
+	buf_putbytes(signbuf, 
+		buf_getptr(ses.payload, sign_payload_length),
+		sign_payload_length);
+	buf_incrpos(ses.payload, sign_payload_length);
+
 	buf_setpos(signbuf, 0);
 
 	/* ... and finally verify the signature */