Author: Stefan Bühler <stbuehler@web.de>
Date:   Fri Jan 22 17:23:03 2010 +0100

    Append to previous buffer in con read (fixes #2147, found by liming, CVE-2010-0295)

diff --git a/src/connections.c b/src/connections.c
index 3f1db10..1069ada 100644
--- a/src/connections.c
+++ b/src/connections.c
@@ -802,8 +802,14 @@ static handler_t connection_handle_read_request_content(server *srv, connection
 		} else {
 			buffer *b;
 
-			b = chunkqueue_get_append_buffer(out);
-			buffer_copy_string_len(b, c->mem->ptr + c->offset, toRead);
+			b = (out->last) ? out->last->mem : NULL;
+
+			if (NULL == b) {
+				b = chunkqueue_get_append_buffer(out);
+				buffer_prepare_copy(b, con->request.content_length - out->bytes_in + 1);
+			}
+
+			buffer_append_string_len(b, c->mem->ptr + c->offset, toRead);
 		}
 
 		c->offset += toRead;
diff --git a/src/network_openssl.c b/src/network_openssl.c
index 0586d14..7ffc8c4 100644
--- a/src/network_openssl.c
+++ b/src/network_openssl.c
@@ -37,16 +37,29 @@ NETWORK_BACKEND_READ(openssl) {
 	off_t max_read = 256 * 1024;
 	off_t start_bytes_in = cq->bytes_in;
 	network_status_t res;
+	int toread, read_offset;
 
 	UNUSED(srv);
 	UNUSED(con);
 
+	/* use a chunk-size of 4k, append to last buffer if it has >= 1kb free */
+#define TOREAD 4096
+
 	do {
 		int oerrno;
-		b = chunkqueue_get_append_buffer(cq);
-		buffer_prepare_copy(b, 8192 + 12); /* ssl-chunk-size is 8kb */
+
+		b = (cq->last) ? cq->last->mem : NULL;
+
+		if (NULL == b || b->size - b->used < 1024) {
+			b = chunkqueue_get_append_buffer(cq);
+			buffer_prepare_copy(b, TOREAD+1);
+		}
+
+		read_offset = (b->used == 0) ? 0 : b->used - 1;
+		toread = b->size - 1 - read_offset;
+
 		ERR_clear_error();
-		len = SSL_read(sock->ssl, b->ptr, b->size - 1);
+		len = SSL_read(sock->ssl, b->ptr + read_offset, toread);
 
 		/**
 		 * man SSL_read:
@@ -63,6 +76,7 @@ NETWORK_BACKEND_READ(openssl) {
 
 			switch ((r = SSL_get_error(sock->ssl, len))) {
 			case SSL_ERROR_WANT_READ:
+			case SSL_ERROR_WANT_WRITE:
 				return read_something ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT;
 			case SSL_ERROR_SYSCALL:
 				/**
@@ -123,6 +137,7 @@ NETWORK_BACKEND_READ(openssl) {
 				return res;
 			}
 		} else {
+			if (b->used > 0) b->used--;
 			b->used += len;
 			b->ptr[b->used++] = '\0';
 
@@ -130,10 +145,10 @@ NETWORK_BACKEND_READ(openssl) {
 			cq->bytes_in += len;
 		}
 
-		if (cq->bytes_in - start_bytes_in > max_read) return NETWORK_STATUS_SUCCESS;
-	} while (1);
+		if (cq->bytes_in - start_bytes_in > max_read) break;
+	} while (len == toread);
 
-	return NETWORK_STATUS_FATAL_ERROR;
+	return NETWORK_STATUS_SUCCESS;
 }
 
 
diff --git a/src/network_win32_send.c b/src/network_win32_send.c
index b6d5930..0ee3f4a 100644
--- a/src/network_win32_send.c
+++ b/src/network_win32_send.c
@@ -30,7 +30,7 @@
 */
 NETWORK_BACKEND_READ(win32recv)
 {
-	int toread;
+	int toread, read_offset;
 	buffer *b;
 	off_t r, start_bytes_in;
 	off_t max_read = 256 * 1024;
@@ -45,15 +45,21 @@ NETWORK_BACKEND_READ(win32recv)
 
 	start_bytes_in = cq->bytes_in;
 
-	/* use a chunk-size of 16k */
+	/* use a chunk-size of 4k, append to last buffer if it has >= 1kb free */
+#define TOREAD 4096
+
 	do {
-		toread = 16384;
+		b = (cq->last) ? cq->last->mem : NULL;
 
-		b = chunkqueue_get_append_buffer(cq);
+		if (NULL == b || b->size - b->used < 1024) {
+			b = chunkqueue_get_append_buffer(cq);
+			buffer_prepare_copy(b, TOREAD+1);
+		}
 
-		buffer_prepare_copy(b, toread);
+		read_offset = (b->used == 0) ? 0 : b->used - 1;
+		toread = b->size - 1 - read_offset;
 
-		if (-1 == (r = recv(sock->fd, b->ptr, toread, 0))) {
+		if (-1 == (r = recv(sock->fd, b->ptr + read_offset, toread, 0))) {
 			switch (light_sock_errno()) {
 			case EAGAIN:
 			case EWOULDBLOCK:
@@ -76,17 +82,19 @@ NETWORK_BACKEND_READ(win32recv)
 
 		read_something = 1;
 
-		b->used = r;
+		if (b->used > 0) b->used--;
+		b->used += r;
 		b->ptr[b->used++] = '\0';
+
 		cq->bytes_in += r;
 
 		if (cq->bytes_in - start_bytes_in > max_read) break;
-	} while (r == toread); 
+	} while (r == toread);
 
 	return NETWORK_STATUS_SUCCESS;
 }
 
-NETWORK_BACKEND_WRITE(win32send) 
+NETWORK_BACKEND_WRITE(win32send)
 {
 	chunk *c;
 	size_t chunks_written = 0;
diff --git a/src/network_write.c b/src/network_write.c
index 58de720..5de9192 100644
--- a/src/network_write.c
+++ b/src/network_write.c
@@ -35,7 +35,7 @@
 * as vectors
 */
 NETWORK_BACKEND_READ(read) {
-	int toread;
+	int toread, read_offset;
 	buffer *b;
 	off_t r, start_bytes_in;
 	off_t max_read = 256 * 1024;
@@ -50,15 +50,21 @@ NETWORK_BACKEND_READ(read) {
 
 	start_bytes_in = cq->bytes_in;
 
-	/* use a chunk-size of 16k */
+	/* use a chunk-size of 4k, append to last buffer if it has >= 1kb free */
+#define TOREAD 4096
+
 	do {
-		toread = 16384;
+		b = (cq->last) ? cq->last->mem : NULL;
 
-		b = chunkqueue_get_append_buffer(cq);
+		if (NULL == b || b->size - b->used < 1024) {
+			b = chunkqueue_get_append_buffer(cq);
+			buffer_prepare_copy(b, TOREAD+1);
+		}
 
-		buffer_prepare_copy(b, toread);
+		read_offset = (b->used == 0) ? 0 : b->used - 1;
+		toread = b->size - 1 - read_offset;
 
-		if (-1 == (r = read(sock->fd, b->ptr, toread))) {
+		if (-1 == (r = read(sock->fd, b->ptr + read_offset, toread))) {
 			switch (errno) {
 			case EAGAIN:
 				/* remove the last chunk from the chunkqueue */
@@ -80,8 +86,10 @@ NETWORK_BACKEND_READ(read) {
 
 		read_something = 1;
 
-		b->used = r;
+		if (b->used > 0) b->used--;
+		b->used += r;
 		b->ptr[b->used++] = '\0';
+
 		cq->bytes_in += r;
 
 		if (cq->bytes_in - start_bytes_in > max_read) break;
