# HG changeset patch
# Parent  0697f5e726f65e465fe2b21f543557b95bb62597
Pre-allocare buffer for private keys data to prevent leaking of sensitive data
via heap.

CVE-2016-10011
bsc#1016369

backported upstream commit 54d022026aae4f53fa74cc636e4a032d9689b64d
backported upstream commit a9c746088787549bb5b1ae3add7d06a1b6d93d5e

diff --git a/openssh-7.2p2/authfile.c b/openssh-7.2p2/authfile.c
--- a/openssh-7.2p2/authfile.c
+++ b/openssh-7.2p2/authfile.c
@@ -95,23 +95,35 @@ sshkey_save_private(struct sshkey *key, 
 
 /* Load a key from a fd into a buffer */
 int
 sshkey_load_file(int fd, struct sshbuf *blob)
 {
 	u_char buf[1024];
 	size_t len;
 	struct stat st;
-	int r;
+	int r, dontmax = 0;
 
 	if (fstat(fd, &st) < 0)
 		return SSH_ERR_SYSTEM_ERROR;
 	if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 &&
 	    st.st_size > MAX_KEY_FILE_SIZE)
 		return SSH_ERR_INVALID_FORMAT;
+	/*
+	 * Pre-allocate the buffer used for the key contents and clamp its
+	 * maximum size. This ensures that key contents are never leaked via
+	 * implicit realloc() in the sshbuf code.
+	 */
+	if ((st.st_mode & S_IFREG) == 0 || st.st_size <= 0) {
+		st.st_size = 64*1024; /* 64k should be enough for anyone :) */
+		dontmax = 1;
+	}
+	if ((r = sshbuf_allocate(blob, st.st_size)) != 0 ||
+	    (dontmax && (r = sshbuf_set_max_size(blob, st.st_size)) != 0))
+		return r;
 	for (;;) {
 		if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) {
 			if (errno == EPIPE)
 				break;
 			r = SSH_ERR_SYSTEM_ERROR;
 			goto out;
 		}
 		if ((r = sshbuf_put(blob, buf, len)) != 0)
diff --git a/openssh-7.2p2/sshbuf.c b/openssh-7.2p2/sshbuf.c
--- a/openssh-7.2p2/sshbuf.c
+++ b/openssh-7.2p2/sshbuf.c
@@ -311,63 +311,73 @@ sshbuf_check_reserve(const struct sshbuf
 	SSHBUF_TELL("check");
 	/* Check that len is reasonable and that max_size + available < len */
 	if (len > buf->max_size || buf->max_size - len < buf->size - buf->off)
 		return SSH_ERR_NO_BUFFER_SPACE;
 	return 0;
 }
 
 int
-sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp)
+sshbuf_allocate(struct sshbuf *buf, size_t len)
 {
 	size_t rlen, need;
 	u_char *dp;
 	int r;
 
-	if (dpp != NULL)
-		*dpp = NULL;
-
-	SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len));
+	SSHBUF_DBG(("allocate buf = %p len = %zu", buf, len));
 	if ((r = sshbuf_check_reserve(buf, len)) != 0)
 		return r;
 	/*
 	 * If the requested allocation appended would push us past max_size
 	 * then pack the buffer, zeroing buf->off.
 	 */
 	sshbuf_maybe_pack(buf, buf->size + len > buf->max_size);
-	SSHBUF_TELL("reserve");
-	if (len + buf->size > buf->alloc) {
-		/*
-		 * Prefer to alloc in SSHBUF_SIZE_INC units, but
-		 * allocate less if doing so would overflow max_size.
-		 */
-		need = len + buf->size - buf->alloc;
-		rlen = roundup(buf->alloc + need, SSHBUF_SIZE_INC);
-		SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen));
-		if (rlen > buf->max_size)
-			rlen = buf->alloc + need;
-		SSHBUF_DBG(("adjusted rlen %zu", rlen));
-		if ((dp = realloc(buf->d, rlen)) == NULL) {
-			SSHBUF_DBG(("realloc fail"));
-			if (dpp != NULL)
-				*dpp = NULL;
-			return SSH_ERR_ALLOC_FAIL;
-		}
-		buf->alloc = rlen;
-		buf->cd = buf->d = dp;
-		if ((r = sshbuf_check_reserve(buf, len)) < 0) {
-			/* shouldn't fail */
-			if (dpp != NULL)
-				*dpp = NULL;
-			return r;
-		}
+	SSHBUF_TELL("allocate");
+	if (len + buf->size <= buf->alloc)
+		return 0; /* already have it. */
+
+	/*
+	 * Prefer to alloc in SSHBUF_SIZE_INC units, but
+	 * allocate less if doing so would overflow max_size.
+	 */
+	need = len + buf->size - buf->alloc;
+	rlen = roundup(buf->alloc + need, SSHBUF_SIZE_INC);
+	SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen));
+	if (rlen > buf->max_size)
+		rlen = buf->alloc + need;
+	SSHBUF_DBG(("adjusted rlen %zu", rlen));
+	if ((dp = realloc(buf->d, rlen)) == NULL) {
+		SSHBUF_DBG(("realloc fail"));
+		return SSH_ERR_ALLOC_FAIL;
 	}
+	buf->alloc = rlen;
+	buf->cd = buf->d = dp;
+	if ((r = sshbuf_check_reserve(buf, len)) < 0) {
+		/* shouldn't fail */
+		return r;
+	}
+	SSHBUF_TELL("done");
+	return 0;
+}
+
+int
+sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp)
+{
+	u_char *dp;
+	int r;
+
+	if (dpp != NULL)
+		*dpp = NULL;
+
+	SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len));
+	if ((r = sshbuf_allocate(buf, len)) != 0)
+		return r;
+
 	dp = buf->d + buf->size;
 	buf->size += len;
-	SSHBUF_TELL("done");
 	if (dpp != NULL)
 		*dpp = dp;
 	return 0;
 }
 
 int
 sshbuf_consume(struct sshbuf *buf, size_t len)
 {
diff --git a/openssh-7.2p2/sshbuf.h b/openssh-7.2p2/sshbuf.h
--- a/openssh-7.2p2/sshbuf.h
+++ b/openssh-7.2p2/sshbuf.h
@@ -134,16 +134,24 @@ u_char *sshbuf_mutable_ptr(const struct 
  * Check whether a reservation of size len will succeed in buf
  * Safer to use than direct comparisons again sshbuf_avail as it copes
  * with unsigned overflows correctly.
  * Returns 0 on success, or a negative SSH_ERR_* error code on failure.
  */
 int	sshbuf_check_reserve(const struct sshbuf *buf, size_t len);
 
 /*
+ * Preallocates len additional bytes in buf.
+ * Useful for cases where the caller knows how many bytes will ultimately be
+ * required to avoid realloc in the buffer code.
+ * Returns 0 on success, or a negative SSH_ERR_* error code on failure.
+ */
+int	sshbuf_allocate(struct sshbuf *buf, size_t len);
+
+/*
  * Reserve len bytes in buf.
  * Returns 0 on success and a pointer to the first reserved byte via the
  * optional dpp parameter or a negative * SSH_ERR_* error code on failure.
  */
 int	sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp);
 
 /*
  * Consume len bytes from the start of buf
