- djm@cvs.openbsd.org 2011/05/04 21:15:29
     [authfile.c authfile.h ssh-add.c]
     allow "ssh-add - < key"; feedback and ok markus@
diff --git a/authfile.c b/authfile.c
index a49850c..608d1d0 100644
--- a/authfile.c
+++ b/authfile.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: authfile.c,v 1.87 2010/11/29 18:57:04 markus Exp $ */
+/* $OpenBSD: authfile.c,v 1.88 2011/05/04 21:15:29 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -69,6 +69,8 @@
 #include "misc.h"
 #include "atomicio.h"
 
+#define MAX_KEY_FILE_SIZE	(1024 * 1024)
+
 /* Version identification string for SSH v1 identity files. */
 static const char authfile_id_string[] =
     "SSH PRIVATE KEY FILE FORMAT 1.1\n";
@@ -312,12 +314,12 @@
 	return pub;
 }
 
-/* Load the contents of a key file into a buffer */
-static int
+/* Load a key from a fd into a buffer */
+int
 key_load_file(int fd, const char *filename, Buffer *blob)
 {
+	u_char buf[1024];
 	size_t len;
-	u_char *cp;
 	struct stat st;
 
 	if (fstat(fd, &st) < 0) {
@@ -325,30 +327,45 @@
 		    filename == NULL ? "" : filename,
 		    filename == NULL ? "" : " ",
 		    strerror(errno));
-		close(fd);
 		return 0;
 	}
-	if (st.st_size > 1*1024*1024) {
+	if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 &&
+	    st.st_size > MAX_KEY_FILE_SIZE) {
+ toobig:
 		error("%s: key file %.200s%stoo large", __func__,
 		    filename == NULL ? "" : filename,
 		    filename == NULL ? "" : " ");
-		close(fd);
 		return 0;
 	}
-	len = (size_t)st.st_size;		/* truncated */
-
 	buffer_init(blob);
-	cp = buffer_append_space(blob, len);
-
-	if (atomicio(read, fd, cp, len) != len) {
-		debug("%s: read from key file %.200s%sfailed: %.100s", __func__,
-		    filename == NULL ? "" : filename,
-		    filename == NULL ? "" : " ",
-		    strerror(errno));
+	for (;;) {
+		if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) {
+			if (errno == EPIPE)
+				break;
+			debug("%s: read from key file %.200s%sfailed: %.100s",
+			    __func__, filename == NULL ? "" : filename,
+			    filename == NULL ? "" : " ", strerror(errno));
+			buffer_clear(blob);
+			bzero(buf, sizeof(buf));
+			return 0;
+		}
+		buffer_append(blob, buf, len);
+		if (buffer_len(blob) > MAX_KEY_FILE_SIZE) {
+			buffer_clear(blob);
+			bzero(buf, sizeof(buf));
+			goto toobig;
+		}
+	}
+	bzero(buf, sizeof(buf));
+	if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 &&
+	    st.st_size != buffer_len(blob)) {
+		debug("%s: key file %.200s%schanged size while reading",
+		    __func__, filename == NULL ? "" : filename,
+		    filename == NULL ? "" : " ");
 		buffer_clear(blob);
-		close(fd);
 		return 0;
 	}
+
 	return 1;
 }
 
@@ -670,11 +687,38 @@
 }
 
 Key *
+key_parse_private(Buffer *buffer, const char *filename,
+    const char *passphrase, char **commentp)
+{
+	Key *pub, *prv;
+	Buffer pubcopy;
+
+	buffer_init(&pubcopy);
+	buffer_append(&pubcopy, buffer_ptr(buffer), buffer_len(buffer));
+	/* it's a SSH v1 key if the public key part is readable */
+	pub = key_parse_public_rsa1(&pubcopy, commentp);
+	buffer_free(&pubcopy);
+	if (pub == NULL) {
+		prv = key_parse_private_type(buffer, KEY_UNSPEC,
+		    passphrase, NULL);
+		/* use the filename as a comment for PEM */
+		if (commentp && prv)
+			*commentp = xstrdup(filename);
+	} else {
+		key_free(pub);
+		/* key_parse_public_rsa1() has already loaded the comment */
+		prv = key_parse_private_type(buffer, KEY_RSA1, passphrase,
+		    NULL);
+	}
+	return prv;
+}
+
+Key *
 key_load_private(const char *filename, const char *passphrase,
     char **commentp)
 {
-	Key *pub, *prv;
-	Buffer buffer, pubcopy;
+	Key *prv;
+	Buffer buffer;
 	int fd;
 
 	fd = open(filename, O_RDONLY);
@@ -697,23 +741,7 @@
 	}
 	close(fd);
 
-	buffer_init(&pubcopy);
-	buffer_append(&pubcopy, buffer_ptr(&buffer), buffer_len(&buffer));
-	/* it's a SSH v1 key if the public key part is readable */
-	pub = key_parse_public_rsa1(&pubcopy, commentp);
-	buffer_free(&pubcopy);
-	if (pub == NULL) {
-		prv = key_parse_private_type(&buffer, KEY_UNSPEC,
-		    passphrase, NULL);
-		/* use the filename as a comment for PEM */
-		if (commentp && prv)
-			*commentp = xstrdup(filename);
-	} else {
-		key_free(pub);
-		/* key_parse_public_rsa1() has already loaded the comment */
-		prv = key_parse_private_type(&buffer, KEY_RSA1, passphrase,
-		    NULL);
-	}
+	prv = key_parse_private(&buffer, filename, passphrase, commentp);
 	buffer_free(&buffer);
 	return prv;
 }