=== modified file 'auth-rsa.c'
--- auth-rsa.c	
+++ auth-rsa.c	
@@ -156,13 +156,10 @@
 	char line[SSH_MAX_PUBKEY_BYTES], *file;
 	int allowed = 0;
 	u_int bits;
-	FILE *f;
 	u_long linenum = 0;
 	struct stat st;
 	Key *key;
-
-	/* Temporarily use the user's uid. */
-	temporarily_use_uid(pw);
+	void *keysearch;
 
 	/* The authorized keys. */
 	file = authorized_keys_file(pw);
@@ -170,25 +167,7 @@
 
 	/* Fail quietly if file does not exist */
 	if (stat(file, &st) < 0) {
-		/* Restore the privileged uid. */
-		restore_uid();
 		xfree(file);
-		return (0);
-	}
-	/* Open the file containing the authorized keys. */
-	f = fopen(file, "r");
-	if (!f) {
-		/* Restore the privileged uid. */
-		restore_uid();
-		xfree(file);
-		return (0);
-	}
-	if (options.strict_modes &&
-	    secure_filename(f, file, pw, line, sizeof(line)) != 0) {
-		xfree(file);
-		fclose(f);
-		logit("Authentication refused: %s", line);
-		restore_uid();
 		return (0);
 	}
 
@@ -202,7 +181,10 @@
 	 * found, perform a challenge-response dialog to verify that the
 	 * user really has the corresponding private key.
 	 */
-	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
+	keysearch = keysearch_ctxt_new(pw, options.strict_modes, line, sizeof(line), &linenum);
+	keysearch_add_file(keysearch, file);
+
+	while (read_keyfile_line(keysearch) != -1) {
 		char *cp;
 		char *key_options;
 		int keybits;
@@ -263,12 +245,8 @@
 		break;
 	}
 
-	/* Restore the privileged uid. */
-	restore_uid();
-
-	/* Close the file. */
+	keysearch_ctxt_destroy(keysearch);
 	xfree(file);
-	fclose(f);
 
 	/* return key if allowed */
 	if (allowed && rkey != NULL)

=== modified file 'auth.c'
--- auth.c	
+++ auth.c	
@@ -409,77 +409,6 @@
 }
 
 
-/*
- * Check a given file for security. This is defined as all components
- * of the path to the file must be owned by either the owner of
- * of the file or root and no directories must be group or world writable.
- *
- * XXX Should any specific check be done for sym links ?
- *
- * Takes an open file descriptor, the file name, a uid and and
- * error buffer plus max size as arguments.
- *
- * Returns 0 on success and -1 on failure
- */
-int
-secure_filename(FILE *f, const char *file, struct passwd *pw,
-    char *err, size_t errlen)
-{
-	uid_t uid = pw->pw_uid;
-	char buf[MAXPATHLEN], homedir[MAXPATHLEN];
-	char *cp;
-	int comparehome = 0;
-	struct stat st;
-
-	if (realpath(file, buf) == NULL) {
-		snprintf(err, errlen, "realpath %s failed: %s", file,
-		    strerror(errno));
-		return -1;
-	}
-	if (realpath(pw->pw_dir, homedir) != NULL)
-		comparehome = 1;
-
-	/* check the open file to avoid races */
-	if (fstat(fileno(f), &st) < 0 ||
-	    (st.st_uid != 0 && st.st_uid != uid) ||
-	    (st.st_mode & 022) != 0) {
-		snprintf(err, errlen, "bad ownership or modes for file %s",
-		    buf);
-		return -1;
-	}
-
-	/* for each component of the canonical path, walking upwards */
-	for (;;) {
-		if ((cp = dirname(buf)) == NULL) {
-			snprintf(err, errlen, "dirname() failed");
-			return -1;
-		}
-		strlcpy(buf, cp, sizeof(buf));
-
-		debug3("secure_filename: checking '%s'", buf);
-		if (stat(buf, &st) < 0 ||
-		    (st.st_uid != 0 && st.st_uid != uid) ||
-		    (st.st_mode & 022) != 0) {
-			snprintf(err, errlen,
-			    "bad ownership or modes for directory %s", buf);
-			return -1;
-		}
-
-		/* If are passed the homedir then we can stop */
-		if (comparehome && strcmp(homedir, buf) == 0) {
-			debug3("secure_filename: terminating check at '%s'",
-			    buf);
-			break;
-		}
-		/*
-		 * dirname should always complete with a "/" path,
-		 * but we can be paranoid and check for "." too
-		 */
-		if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0))
-			break;
-	}
-	return 0;
-}
 
 struct passwd *
 getpwnamallow(const char *user)

=== modified file 'auth2-pubkey.c'
--- auth2-pubkey.c	
+++ auth2-pubkey.c	
@@ -171,42 +171,26 @@
 {
 	char line[SSH_MAX_PUBKEY_BYTES];
 	int found_key = 0;
-	FILE *f;
 	u_long linenum = 0;
 	struct stat st;
 	Key *found;
 	char *fp;
-
-	/* Temporarily use the user's uid. */
-	temporarily_use_uid(pw);
+	void *keysearch;
 
 	debug("trying public key file %s", file);
 
 	/* Fail quietly if file does not exist */
 	if (stat(file, &st) < 0) {
-		/* Restore the privileged uid. */
-		restore_uid();
 		return 0;
 	}
-	/* Open the file containing the authorized keys. */
-	f = fopen(file, "r");
-	if (!f) {
-		/* Restore the privileged uid. */
-		restore_uid();
-		return 0;
-	}
-	if (options.strict_modes &&
-	    secure_filename(f, file, pw, line, sizeof(line)) != 0) {
-		fclose(f);
-		logit("Authentication refused: %s", line);
-		restore_uid();
-		return 0;
-	}
+
+	keysearch = keysearch_ctxt_new(pw, options.strict_modes, line, sizeof(line), &linenum);
+	keysearch_add_file(keysearch, file);
 
 	found_key = 0;
 	found = key_new(key->type);
 
-	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
+	while (read_keyfile_line(keysearch) != -1) {
 		char *cp, *key_options = NULL;
 
 		/* Skip leading whitespace, empty and comment lines. */
@@ -247,8 +231,7 @@
 			break;
 		}
 	}
-	restore_uid();
-	fclose(f);
+	keysearch_ctxt_destroy(keysearch);
 	key_free(found);
 	if (!found_key)
 		debug2("key not found");

=== modified file 'authfile.c'
--- authfile.c	
+++ authfile.c	
@@ -603,36 +603,35 @@
 static int
 key_try_load_public(Key *k, const char *filename, char **commentp)
 {
-	FILE *f;
+	void *keysearch;
 	char line[SSH_MAX_PUBKEY_BYTES];
 	char *cp;
 	u_long linenum = 0;
 
-	f = fopen(filename, "r");
-	if (f != NULL) {
-		while (read_keyfile_line(f, filename, line, sizeof(line),
-			    &linenum) != -1) {
-			cp = line;
-			switch (*cp) {
+	keysearch = keysearch_ctxt_new(NULL, 0, line, sizeof(line), &linenum);
+	keysearch_add_file(keysearch, filename);
+
+	while (read_keyfile_line(keysearch) != -1) {
+		cp = line;
+		switch (*cp) {
 			case '#':
 			case '\n':
 			case '\0':
 				continue;
-			}
-			/* Skip leading whitespace. */
-			for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
-				;
-			if (*cp) {
-				if (key_read(k, &cp) == 1) {
-					if (commentp)
-						*commentp=xstrdup(filename);
-					fclose(f);
-					return 1;
-				}
+		}
+		/* Skip leading whitespace. */
+		for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
+			;
+		if (*cp) {
+			if (key_read(k, &cp) == 1) {
+				if (commentp)
+					*commentp=xstrdup(filename);
+				keysearch_ctxt_destroy(keysearch);
+				return 1;
 			}
 		}
-		fclose(f);
-	}
+	}
+	keysearch_ctxt_destroy(keysearch);
 	return 0;
 }
 

=== modified file 'misc.c'
--- misc.c	
+++ misc.c	
@@ -30,9 +30,35 @@
 #include <net/if.h>
 #endif
 
+#ifdef HAVE_LIBGEN_H
+#include "libgen.h"
+#endif
+
 #include "misc.h"
 #include "log.h"
 #include "xmalloc.h"
+#include "uidswap.h"
+
+typedef enum {
+	SOURCE_FILE, SOURCE_PIPE
+} KeySourceType;
+
+struct KeySourceCtxt {
+	FILE *f;
+	const char *target;
+	KeySourceType source_type;
+	struct KeySourceCtxt *next;
+};
+
+struct KeySearchCtxt {
+	struct passwd *pw;
+	int strict;
+	char *buf;
+	size_t bufsz;
+	u_long *lineno;
+	struct KeySourceCtxt *keysource_head;
+	struct KeySourceCtxt *keysource_tail;
+};
 
 /* remove newline at end of string */
 char *
@@ -557,22 +583,216 @@
 }
 
 /*
- * Read an entire line from a public key file into a static buffer, discarding
+ * Check a given file for security. This is defined as all components
+ * of the path to the file must be owned by either the owner of
+ * of the file or root and no directories must be group or world writable.
+ *
+ * XXX Should any specific check be done for sym links ?
+ *
+ * Takes an open file pointer, the file name, a uid and and
+ * error buffer plus max size as arguments.
+ *
+ * Returns 0 on success and -1 on failure
+ */
+int
+secure_filename(FILE *f, const char *file, struct passwd *pw,
+    char *err, size_t errlen)
+{
+	uid_t uid = pw->pw_uid;
+	char buf[MAXPATHLEN], homedir[MAXPATHLEN];
+	char *cp;
+	int comparehome = 0;
+	struct stat st;
+
+	if (realpath(file, buf) == NULL) {
+		snprintf(err, errlen, "realpath %s failed: %s", file,
+		    strerror(errno));
+		return -1;
+	}
+	if (realpath(pw->pw_dir, homedir) != NULL)
+		comparehome = 1;
+
+	/* check the open file to avoid races */
+	if (fstat(fileno(f), &st) < 0 ||
+	    (st.st_uid != 0 && st.st_uid != uid) ||
+	    (st.st_mode & 022) != 0) {
+		snprintf(err, errlen, "bad ownership or modes for file %s",
+		    buf);
+		return -1;
+	}
+
+	/* for each component of the canonical path, walking upwards */
+	for (;;) {
+		if ((cp = dirname(buf)) == NULL) {
+			snprintf(err, errlen, "dirname() failed");
+			return -1;
+		}
+		strlcpy(buf, cp, sizeof(buf));
+
+		debug3("secure_filename: checking '%s'", buf);
+		if (stat(buf, &st) < 0 ||
+		    (st.st_uid != 0 && st.st_uid != uid) ||
+		    (st.st_mode & 022) != 0) {
+			snprintf(err, errlen,
+			    "bad ownership or modes for directory %s", buf);
+			return -1;
+		}
+
+		/* If are passed the homedir then we can stop */
+		if (comparehome && strcmp(homedir, buf) == 0) {
+			debug3("secure_filename: terminating check at '%s'",
+			    buf);
+			break;
+		}
+		/*
+		 * dirname should always complete with a "/" path,
+		 * but we can be paranoid and check for "." too
+		 */
+		if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0))
+			break;
+	}
+	return 0;
+}
+
+void *
+keysearch_ctxt_new(struct passwd *pw, int strict, char *buf, size_t bufsz, u_long *lineno)
+{
+	struct KeySearchCtxt *ctxt = xmalloc(sizeof(*ctxt));
+
+	ctxt->pw = pw;
+	ctxt->strict = strict;
+	ctxt->buf = buf;
+	ctxt->bufsz = bufsz;
+	ctxt->lineno = lineno;
+	ctxt->keysource_head = NULL;
+	ctxt->keysource_tail = NULL;
+
+	return ctxt;
+}
+
+static struct KeySourceCtxt *
+keysearch_next_source(struct KeySearchCtxt *ctxt)
+{
+	struct KeySourceCtxt *keysource = ctxt->keysource_head;
+
+	if (!keysource)
+		return NULL;
+
+	if (keysource->f) {
+		if (keysource->source_type == SOURCE_FILE)
+			fclose(keysource->f);
+		else if (keysource->source_type == SOURCE_PIPE)
+			pclose(keysource->f);
+		keysource->f = NULL;
+	}
+
+	keysource = keysource->next;
+	xfree(ctxt->keysource_head);
+	ctxt->keysource_head = keysource;
+
+	return keysource;
+}
+
+void
+keysearch_ctxt_destroy(void *ptr)
+{
+	struct KeySearchCtxt *ctxt = (struct KeySearchCtxt *)ptr;
+	struct KeySourceCtxt *keysource = ctxt->keysource_head;
+
+	while (keysource)
+		keysource = keysearch_next_source(ctxt);
+
+	xfree(ptr);
+}
+
+static void
+keysearch_add_source(void *ptr, const char *target, KeySourceType source_type)
+{
+	struct KeySearchCtxt *ctxt = (struct KeySearchCtxt *)ptr;
+	struct KeySourceCtxt *keysource = xmalloc(sizeof(*keysource));
+
+	keysource->target = target;
+	keysource->source_type = source_type;
+	keysource->f = NULL;
+	keysource->next = NULL;
+
+	if (ctxt->keysource_tail) {
+		ctxt->keysource_tail->next = keysource;
+		ctxt->keysource_tail = keysource;
+	} else {
+		ctxt->keysource_head = keysource;
+		ctxt->keysource_tail = keysource;
+	}
+}
+
+void
+keysearch_add_file(void *ptr, const char *filename)
+{
+	keysearch_add_source(ptr, filename, SOURCE_FILE);
+}
+
+void
+keysearch_add_pipe(void *ptr, const char *command)
+{
+	keysearch_add_source(ptr, command, SOURCE_PIPE);
+}
+
+/*
+ * Read an entire line from a public key source into a static buffer, discarding
  * lines that exceed the buffer size.  Returns 0 on success, -1 on failure.
  */
 int
-read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz,
-   u_long *lineno)
-{
-	while (fgets(buf, bufsz, f) != NULL) {
-		(*lineno)++;
-		if (buf[strlen(buf) - 1] == '\n' || feof(f)) {
+read_keyfile_line(void *ptr)
+{
+	struct KeySearchCtxt *ctxt = (struct KeySearchCtxt *)ptr;
+	struct KeySourceCtxt *keysource = ctxt->keysource_head;
+
+	while (keysource && !keysource->f) {
+		/* FIXME: Random length... */
+		char error[1024];
+
+		if (ctxt->pw)
+			temporarily_use_uid(ctxt->pw);
+
+		if (keysource->source_type == SOURCE_FILE)
+			keysource->f = fopen(keysource->target, "r");
+		else if (keysource->source_type == SOURCE_PIPE)
+			keysource->f = popen(keysource->target, "r");
+
+		if (!keysource->f) {
+			keysource = keysearch_next_source(ctxt);
+			continue;
+		}
+
+		/* FIXME: does this make sense on pipes..? */
+		if (ctxt->strict &&
+				keysource->source_type == SOURCE_FILE &&
+				secure_filename(keysource->f, keysource->target, ctxt->pw, error, sizeof(error)) != 0) {
+			logit("Authentication using %s refused: %s", keysource->target, error);
+			keysource = keysearch_next_source(ctxt);
+			continue;
+		}
+
+		if (ctxt->pw)
+			restore_uid();
+	}
+
+	if (!keysource)
+		return -1;
+
+	while (fgets(ctxt->buf, ctxt->bufsz, keysource->f) != NULL) {
+		(*(ctxt->lineno))++;
+		if (feof(keysource->f)) {
+			keysearch_next_source(ctxt);
+			return 0;
+		}
+		else if (ctxt->buf[strlen(ctxt->buf) - 1] == '\n') {
 			return 0;
 		} else {
 			debug("%s: %s line %lu exceeds size limit", __func__,
-			    filename, *lineno);
+			    keysource->target, *(ctxt->lineno));
 			/* discard remainder of line */
-			while (fgetc(f) != '\n' && !feof(f))
+			while (fgetc(keysource->f) != '\n' && !feof(keysource->f))
 				;	/* nothing */
 		}
 	}
@@ -688,3 +908,4 @@
 	return (r);
 }
 
+/* key files */

=== modified file 'misc.h'
--- misc.h	
+++ misc.h	
@@ -53,7 +53,11 @@
 
 char	*read_passphrase(const char *, int);
 int	 ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
-int	 read_keyfile_line(FILE *, const char *, char *, size_t, u_long *);
+void	*keysearch_ctxt_new(struct passwd *pw, int strict, char *buf, size_t bufsz, u_long *lineno);
+void	 keysearch_ctxt_destroy(void *ptr);
+void	 keysearch_add_file(void *ptr, const char *filename);
+void	 keysearch_add_pipe(void *ptr, const char *command);
+int	 read_keyfile_line(void *ptr);
 
 int	 tun_open(int, int);
 

