[openssh-commits] [openssh] 07/20: upstream commit

git+noreply at mindrot.org git+noreply at mindrot.org
Tue Jan 20 00:27:51 EST 2015


This is an automated email from the git hooks/post-receive script.

djm pushed a commit to branch master
in repository openssh.

commit c29811cc480a260e42fd88849fc86a80c1e91038
Author: djm at openbsd.org <djm at openbsd.org>
Date:   Sun Jan 18 21:40:23 2015 +0000

    upstream commit
    
    introduce hostkeys_foreach() to allow iteration over a
     known_hosts file or controlled subset thereof. This will allow us to pull out
     some ugly and duplicated code, and will be used to implement hostkey rotation
     later.
    
    feedback and ok markus
---
 hostfile.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 hostfile.h |  43 +++++++++++++++++-
 2 files changed, 187 insertions(+), 3 deletions(-)

diff --git a/hostfile.c b/hostfile.c
index 40dbbd4..5f03663 100644
--- a/hostfile.c
+++ b/hostfile.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: hostfile.c,v 1.59 2015/01/15 09:40:00 djm Exp $ */
+/* $OpenBSD: hostfile.c,v 1.60 2015/01/18 21:40:23 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -42,6 +42,7 @@
 
 #include <netinet/in.h>
 
+#include <errno.h>
 #include <resolv.h>
 #include <stdarg.h>
 #include <stdio.h>
@@ -64,6 +65,8 @@ struct hostkeys {
 	u_int num_entries;
 };
 
+/* XXX hmac is too easy to dictionary attack; use bcrypt? */
+
 static int
 extract_salt(const char *s, u_int l, u_char *salt, size_t salt_len)
 {
@@ -496,7 +499,147 @@ add_host_to_hostfile(const char *filename, const char *host,
 		    __func__, filename, ssh_err(r));
 	} else
 		success = 1;
-	fputs("\n", f);
+	fputc('\n', f);
 	fclose(f);
 	return success;
 }
+
+static int
+match_maybe_hashed(const char *host, const char *names, int *was_hashed)
+{
+	int hashed = *names == HASH_DELIM;
+	const char *hashed_host;
+	size_t nlen = strlen(names);
+
+	if (was_hashed != NULL)
+		*was_hashed = hashed;
+	if (hashed) {
+		if ((hashed_host = host_hash(host, names, nlen)) == NULL)
+			return -1;
+		return nlen == strlen(hashed_host) &&
+		    strncmp(hashed_host, names, nlen) == 0;
+	}
+	return match_hostname(host, names, nlen) == 1;
+}
+
+int
+hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx,
+    const char *host, u_int options)
+{
+	FILE *f;
+	char line[8192], oline[8192];
+	u_long linenum = 0;
+	char *cp, *cp2;
+	u_int kbits;
+	int s, r = 0;
+	struct hostkey_foreach_line lineinfo;
+
+	memset(&lineinfo, 0, sizeof(lineinfo));
+	if (host == NULL && (options & HKF_WANT_MATCH_HOST) != 0)
+		return SSH_ERR_INVALID_ARGUMENT;
+	if ((f = fopen(path, "r")) == NULL)
+		return SSH_ERR_SYSTEM_ERROR;
+
+	debug3("%s: reading file \"%s\"", __func__, path);
+	while (read_keyfile_line(f, path, line, sizeof(line), &linenum) == 0) {
+		line[strcspn(line, "\n")] = '\0';
+		strlcpy(oline, line, sizeof(oline));
+
+		sshkey_free(lineinfo.key);
+		memset(&lineinfo, 0, sizeof(lineinfo));
+		lineinfo.path = path;
+		lineinfo.linenum = linenum;
+		lineinfo.line = oline;
+		lineinfo.status = HKF_STATUS_OK;
+
+		/* Skip any leading whitespace, comments and empty lines. */
+		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
+			;
+		if (!*cp || *cp == '#' || *cp == '\n') {
+			if ((options & HKF_WANT_MATCH_HOST) == 0) {
+				lineinfo.status = HKF_STATUS_COMMENT;
+				if ((r = callback(&lineinfo, ctx)) != 0)
+					break;
+			}
+			continue;
+		}
+
+		if ((lineinfo.marker = check_markers(&cp)) == MRK_ERROR) {
+			verbose("%s: invalid marker at %s:%lu",
+			    __func__, path, linenum);
+			if ((options & HKF_WANT_MATCH_HOST) == 0)
+				goto bad;
+			continue;
+		}
+
+		/* Find the end of the host name portion. */
+		for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
+			;
+		lineinfo.hosts = cp;
+		*cp2++ = '\0';
+
+		/* Check if the host name matches. */
+		if (host != NULL) {
+			s = match_maybe_hashed(host, lineinfo.hosts,
+			    &lineinfo.was_hashed);
+			if (s == 1)
+				lineinfo.status = HKF_STATUS_HOST_MATCHED;
+			else if ((options & HKF_WANT_MATCH_HOST) != 0)
+				continue;
+			else if (s == -1) {
+				debug2("%s: %s:%ld: bad host hash \"%.32s\"",
+				    __func__, path, linenum, lineinfo.hosts);
+				goto bad;
+			}
+		}
+
+		/* Got a match.  Skip host name and any following whitespace */
+		for (; *cp2 == ' ' || *cp2 == '\t'; cp2++)
+			;
+		if (*cp2 == '\0' || *cp2 == '#') {
+			debug2("%s:%ld: truncated before key", path, linenum);
+			goto bad;
+		}
+		lineinfo.rawkey = cp = cp2;
+
+		if ((options & HKF_WANT_PARSE_KEY) != 0) {
+			/*
+			 * Extract the key from the line.  This will skip
+			 * any leading whitespace.  Ignore badly formatted
+			 * lines.
+			 */
+			if ((lineinfo.key = sshkey_new(KEY_UNSPEC)) == NULL) {
+				error("%s: sshkey_new failed", __func__);
+				return SSH_ERR_ALLOC_FAIL;
+			}
+			if (!hostfile_read_key(&cp, &kbits, lineinfo.key)) {
+#ifdef WITH_SSH1
+				sshkey_free(lineinfo.key);
+				lineinfo.key = sshkey_new(KEY_RSA1);
+				if (lineinfo.key  == NULL) {
+					error("%s: sshkey_new fail", __func__);
+					return SSH_ERR_ALLOC_FAIL;
+				}
+				if (!hostfile_read_key(&cp, &kbits,
+				    lineinfo.key))
+					goto bad;
+#else
+				goto bad;
+#endif
+			}
+			if (!hostfile_check_key(kbits, lineinfo.key, host,
+			    path, linenum)) {
+ bad:
+				lineinfo.status = HKF_STATUS_INVALID;
+				if ((r = callback(&lineinfo, ctx)) != 0)
+					break;
+				continue;
+			}
+		}
+		if ((r = callback(&lineinfo, ctx)) != 0)
+			break;
+	}
+	sshkey_free(lineinfo.key);
+	fclose(f);
+	return r;
+}
diff --git a/hostfile.h b/hostfile.h
index d90973f..24c3813 100644
--- a/hostfile.h
+++ b/hostfile.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: hostfile.h,v 1.21 2015/01/15 09:40:00 djm Exp $ */
+/* $OpenBSD: hostfile.h,v 1.22 2015/01/18 21:40:24 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
@@ -52,4 +52,45 @@ int	 add_host_to_hostfile(const char *, const char *,
 
 char	*host_hash(const char *, const char *, u_int);
 
+/*
+ * Iterate through a hostkeys file, optionally parsing keys and matching
+ * hostnames. Allows access to the raw keyfile lines to allow
+ * streaming edits to the file to take place.
+ */
+#define HKF_WANT_MATCH_HOST	(1)	/* return only matching hosts */
+#define HKF_WANT_PARSE_KEY	(1<<1)	/* need key parsed */
+
+#define HKF_STATUS_OK		1	/* Line parsed, didn't match host */
+#define HKF_STATUS_INVALID	2	/* line had parse error */
+#define HKF_STATUS_COMMENT	3	/* valid line contained no key */
+#define HKF_STATUS_HOST_MATCHED	4	/* hostname matched */
+
+/*
+ * The callback function receives this as an argument for each matching 
+ * hostkey line. The callback may "steal" the 'key' field by setting it to NULL.
+ * If a parse error occurred, then "hosts" and subsequent options may be NULL.
+ */
+struct hostkey_foreach_line {
+	const char *path; /* Path of file */
+	u_long linenum;	/* Line number */
+	int status;	/* One of HKF_STATUS_* */
+	char *line;	/* Entire key line; mutable by callback */
+	int marker;	/* CA/revocation markers; indicated by MRK_* value */
+	const char *hosts; /* Raw hosts text, may be hashed or list multiple */
+	int was_hashed;	/* Non-zero if hostname was hashed */
+	const char *rawkey; /* Text of key and any comment following it */
+	struct sshkey *key; /* Key, if parsed ok and HKF_WANT_MATCH_HOST set */
+	const char *comment; /* Any comment following the key */
+};
+
+/*
+ * Callback fires for each line (or matching line if a HKF_WANT_* option
+ * is set). The foreach loop will terminate if the callback returns a non-
+ * zero exit status.
+ */
+typedef int hostkeys_foreach_fn(struct hostkey_foreach_line *l, void *ctx);
+
+int hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx,
+    const char *host, u_int options);
+
 #endif

-- 
To stop receiving notification emails like this one, please contact
djm at mindrot.org.


More information about the openssh-commits mailing list