[openssh-commits] [openssh] 02/11: upstream: allow key revocation by SHA256 hash and allow ssh-keygen

git+noreply at mindrot.org git+noreply at mindrot.org
Wed Sep 12 16:52:13 AEST 2018


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

djm pushed a commit to branch master
in repository openssh.

commit 9405c6214f667be604a820c6823b27d0ea77937d
Author: djm at openbsd.org <djm at openbsd.org>
Date:   Wed Sep 12 01:21:34 2018 +0000

    upstream: allow key revocation by SHA256 hash and allow ssh-keygen
    
    to create KRLs using SHA256/base64 key fingerprints; ok markus@
    
    OpenBSD-Commit-ID: a0590fd34e7f1141f2873ab3acc57442560e6a94
---
 PROTOCOL.krl |  16 ++++----
 krl.c        | 126 +++++++++++++++++++++++++++++++++++++++++++++--------------
 krl.h        |   6 ++-
 ssh-keygen.1 |  19 +++++++--
 ssh-keygen.c |  75 +++++++++++++++++++++++++++++++----
 5 files changed, 193 insertions(+), 49 deletions(-)

diff --git a/PROTOCOL.krl b/PROTOCOL.krl
index f319bad2..115f80e5 100644
--- a/PROTOCOL.krl
+++ b/PROTOCOL.krl
@@ -36,6 +36,7 @@ The available section types are:
 #define KRL_SECTION_EXPLICIT_KEY		2
 #define KRL_SECTION_FINGERPRINT_SHA1		3
 #define KRL_SECTION_SIGNATURE			4
+#define KRL_SECTION_FINGERPRINT_SHA256		5
 
 2. Certificate section
 
@@ -127,18 +128,19 @@ must be a raw key (i.e. not a certificate).
 
 This section may appear multiple times.
 
-4. SHA1 fingerprint sections
+4. SHA1/SHA256 fingerprint sections
 
-These sections, identified as KRL_SECTION_FINGERPRINT_SHA1, revoke
-plain keys (i.e. not certificates) by listing their SHA1 hashes:
+These sections, identified as KRL_SECTION_FINGERPRINT_SHA1 and
+KRL_SECTION_FINGERPRINT_SHA256, revoke plain keys (i.e. not
+certificates) by listing their hashes:
 
 	string	public_key_hash[0]
 	....
 
 This section must contain at least one "public_key_hash". The hash blob
-is obtained by taking the SHA1 hash of the public key blob. Hashes in
-this section must appear in numeric order, treating each hash as a big-
-endian integer.
+is obtained by taking the SHA1 or SHA256 hash of the public key blob.
+Hashes in this section must appear in numeric order, treating each hash
+as a big-endian integer.
 
 This section may appear multiple times.
 
@@ -166,4 +168,4 @@ Implementations that retrieve KRLs over untrusted channels must verify
 signatures. Signature sections are optional for KRLs distributed by
 trusted means.
 
-$OpenBSD: PROTOCOL.krl,v 1.4 2018/04/10 00:10:49 djm Exp $
+$OpenBSD: PROTOCOL.krl,v 1.5 2018/09/12 01:21:34 djm Exp $
diff --git a/krl.c b/krl.c
index 37915324..8e2d5d5d 100644
--- a/krl.c
+++ b/krl.c
@@ -14,7 +14,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $OpenBSD: krl.c,v 1.41 2017/12/18 02:25:15 djm Exp $ */
+/* $OpenBSD: krl.c,v 1.42 2018/09/12 01:21:34 djm Exp $ */
 
 #include "includes.h"
 
@@ -96,6 +96,7 @@ struct ssh_krl {
 	char *comment;
 	struct revoked_blob_tree revoked_keys;
 	struct revoked_blob_tree revoked_sha1s;
+	struct revoked_blob_tree revoked_sha256s;
 	struct revoked_certs_list revoked_certs;
 };
 
@@ -136,6 +137,7 @@ ssh_krl_init(void)
 		return NULL;
 	RB_INIT(&krl->revoked_keys);
 	RB_INIT(&krl->revoked_sha1s);
+	RB_INIT(&krl->revoked_sha256s);
 	TAILQ_INIT(&krl->revoked_certs);
 	return krl;
 }
@@ -178,6 +180,11 @@ ssh_krl_free(struct ssh_krl *krl)
 		free(rb->blob);
 		free(rb);
 	}
+	RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_sha256s, trb) {
+		RB_REMOVE(revoked_blob_tree, &krl->revoked_sha256s, rb);
+		free(rb->blob);
+		free(rb);
+	}
 	TAILQ_FOREACH_SAFE(rc, &krl->revoked_certs, entry, trc) {
 		TAILQ_REMOVE(&krl->revoked_certs, rc, entry);
 		revoked_certs_free(rc);
@@ -408,25 +415,47 @@ ssh_krl_revoke_key_explicit(struct ssh_krl *krl, const struct sshkey *key)
 	return revoke_blob(&krl->revoked_keys, blob, len);
 }
 
-int
-ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const struct sshkey *key)
+static int
+revoke_by_hash(struct revoked_blob_tree *target, const u_char *p, size_t len)
 {
 	u_char *blob;
-	size_t len;
 	int r;
 
-	debug3("%s: revoke type %s by sha1", __func__, sshkey_type(key));
-	if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA1,
-	    &blob, &len)) != 0)
+	/* need to copy hash, as revoke_blob steals ownership */
+	if ((blob = malloc(len)) == NULL)
+		return SSH_ERR_SYSTEM_ERROR;
+	memcpy(blob, p, len);
+	if ((r = revoke_blob(target, blob, len)) != 0) {
+		free(blob);
 		return r;
-	return revoke_blob(&krl->revoked_sha1s, blob, len);
+	}
+	return 0;
+}
+
+int
+ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const u_char *p, size_t len)
+{
+	debug3("%s: revoke by sha1", __func__);
+	if (len != 20)
+		return SSH_ERR_INVALID_FORMAT;
+	return revoke_by_hash(&krl->revoked_sha1s, p, len);
+}
+
+int
+ssh_krl_revoke_key_sha256(struct ssh_krl *krl, const u_char *p, size_t len)
+{
+	debug3("%s: revoke by sha256", __func__);
+	if (len != 32)
+		return SSH_ERR_INVALID_FORMAT;
+	return revoke_by_hash(&krl->revoked_sha256s, p, len);
 }
 
 int
 ssh_krl_revoke_key(struct ssh_krl *krl, const struct sshkey *key)
 {
+	/* XXX replace with SHA256? */
 	if (!sshkey_is_cert(key))
-		return ssh_krl_revoke_key_sha1(krl, key);
+		return ssh_krl_revoke_key_explicit(krl, key);
 
 	if (key->cert->serial == 0) {
 		return ssh_krl_revoke_cert_by_key_id(krl,
@@ -762,6 +791,18 @@ ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf,
 		    (r = sshbuf_put_stringb(buf, sect)) != 0)
 			goto out;
 	}
+	sshbuf_reset(sect);
+	RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha256s) {
+		KRL_DBG(("%s: hash len %zu ", __func__, rb->len));
+		if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0)
+			goto out;
+	}
+	if (sshbuf_len(sect) != 0) {
+		if ((r = sshbuf_put_u8(buf,
+		    KRL_SECTION_FINGERPRINT_SHA256)) != 0 ||
+		    (r = sshbuf_put_stringb(buf, sect)) != 0)
+			goto out;
+	}
 
 	for (i = 0; i < nsign_keys; i++) {
 		KRL_DBG(("%s: signature key %s", __func__,
@@ -914,6 +955,29 @@ parse_revoked_certs(struct sshbuf *buf, struct ssh_krl *krl)
 	return r;
 }
 
+static int
+blob_section(struct sshbuf *sect, struct revoked_blob_tree *target_tree,
+    size_t expected_len)
+{
+	u_char *rdata = NULL;
+	size_t rlen = 0;
+	int r;
+
+	while (sshbuf_len(sect) > 0) {
+		if ((r = sshbuf_get_string(sect, &rdata, &rlen)) != 0)
+			return r;
+		if (expected_len != 0 && rlen != expected_len) {
+			error("%s: bad length", __func__);
+			free(rdata);
+			return SSH_ERR_INVALID_FORMAT;
+		}
+		if ((r = revoke_blob(target_tree, rdata, rlen)) != 0) {
+			free(rdata);
+			return r;
+		}
+	}
+	return 0;
+}
 
 /* Attempt to parse a KRL, checking its signature (if any) with sign_ca_keys. */
 int
@@ -925,9 +989,9 @@ ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp,
 	char timestamp[64];
 	int r = SSH_ERR_INTERNAL_ERROR, sig_seen;
 	struct sshkey *key = NULL, **ca_used = NULL, **tmp_ca_used;
-	u_char type, *rdata = NULL;
+	u_char type;
 	const u_char *blob;
-	size_t i, j, sig_off, sects_off, rlen, blen, nca_used;
+	size_t i, j, sig_off, sects_off, blen, nca_used;
 	u_int format_version;
 
 	nca_used = 0;
@@ -1068,24 +1132,19 @@ ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp,
 				goto out;
 			break;
 		case KRL_SECTION_EXPLICIT_KEY:
+			if ((r = blob_section(sect,
+			    &krl->revoked_keys, 0)) != 0)
+				goto out;
+			break;
 		case KRL_SECTION_FINGERPRINT_SHA1:
-			while (sshbuf_len(sect) > 0) {
-				if ((r = sshbuf_get_string(sect,
-				    &rdata, &rlen)) != 0)
-					goto out;
-				if (type == KRL_SECTION_FINGERPRINT_SHA1 &&
-				    rlen != 20) {
-					error("%s: bad SHA1 length", __func__);
-					r = SSH_ERR_INVALID_FORMAT;
-					goto out;
-				}
-				if ((r = revoke_blob(
-				    type == KRL_SECTION_EXPLICIT_KEY ?
-				    &krl->revoked_keys : &krl->revoked_sha1s,
-				    rdata, rlen)) != 0)
-					goto out;
-				rdata = NULL; /* revoke_blob frees rdata */
-			}
+			if ((r = blob_section(sect,
+			    &krl->revoked_sha1s, 20)) != 0)
+				goto out;
+			break;
+		case KRL_SECTION_FINGERPRINT_SHA256:
+			if ((r = blob_section(sect,
+			    &krl->revoked_sha256s, 32)) != 0)
+				goto out;
 			break;
 		case KRL_SECTION_SIGNATURE:
 			/* Handled above, but still need to stay in synch */
@@ -1150,7 +1209,6 @@ ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp,
 	for (i = 0; i < nca_used; i++)
 		sshkey_free(ca_used[i]);
 	free(ca_used);
-	free(rdata);
 	sshkey_free(key);
 	sshbuf_free(copy);
 	sshbuf_free(sect);
@@ -1210,6 +1268,16 @@ is_key_revoked(struct ssh_krl *krl, const struct sshkey *key)
 		KRL_DBG(("%s: revoked by key SHA1", __func__));
 		return SSH_ERR_KEY_REVOKED;
 	}
+	memset(&rb, 0, sizeof(rb));
+	if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA256,
+	    &rb.blob, &rb.len)) != 0)
+		return r;
+	erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha256s, &rb);
+	free(rb.blob);
+	if (erb != NULL) {
+		KRL_DBG(("%s: revoked by key SHA256", __func__));
+		return SSH_ERR_KEY_REVOKED;
+	}
 
 	/* Next, explicit keys */
 	memset(&rb, 0, sizeof(rb));
diff --git a/krl.h b/krl.h
index 675496cc..815a1df4 100644
--- a/krl.h
+++ b/krl.h
@@ -14,7 +14,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $OpenBSD: krl.h,v 1.5 2015/12/30 23:46:14 djm Exp $ */
+/* $OpenBSD: krl.h,v 1.6 2018/09/12 01:21:34 djm Exp $ */
 
 #ifndef _KRL_H
 #define _KRL_H
@@ -29,6 +29,7 @@
 #define KRL_SECTION_EXPLICIT_KEY	2
 #define KRL_SECTION_FINGERPRINT_SHA1	3
 #define KRL_SECTION_SIGNATURE		4
+#define KRL_SECTION_FINGERPRINT_SHA256	5
 
 /* KRL_SECTION_CERTIFICATES subsection types */
 #define KRL_SECTION_CERT_SERIAL_LIST	0x20
@@ -51,7 +52,8 @@ int ssh_krl_revoke_cert_by_serial_range(struct ssh_krl *krl,
 int ssh_krl_revoke_cert_by_key_id(struct ssh_krl *krl,
     const struct sshkey *ca_key, const char *key_id);
 int ssh_krl_revoke_key_explicit(struct ssh_krl *krl, const struct sshkey *key);
-int ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const struct sshkey *key);
+int ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const u_char *p, size_t len);
+int ssh_krl_revoke_key_sha256(struct ssh_krl *krl, const u_char *p, size_t len);
 int ssh_krl_revoke_key(struct ssh_krl *krl, const struct sshkey *key);
 int ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf,
     const struct sshkey **sign_keys, u_int nsign_keys);
diff --git a/ssh-keygen.1 b/ssh-keygen.1
index dd6e7e5a..d1aad6f2 100644
--- a/ssh-keygen.1
+++ b/ssh-keygen.1
@@ -1,4 +1,4 @@
-.\"	$OpenBSD: ssh-keygen.1,v 1.148 2018/08/08 01:16:01 djm Exp $
+.\"	$OpenBSD: ssh-keygen.1,v 1.149 2018/09/12 01:21:34 djm Exp $
 .\"
 .\" Author: Tatu Ylonen <ylo at cs.hut.fi>
 .\" Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -35,7 +35,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: August 8 2018 $
+.Dd $Mdocdate: September 12 2018 $
 .Dt SSH-KEYGEN 1
 .Os
 .Sh NAME
@@ -814,7 +814,20 @@ option.
 Revokes the specified key.
 If a certificate is listed, then it is revoked as a plain public key.
 .It Cm sha1 : Ar public_key
-Revokes the specified key by its SHA1 hash.
+Revokes the specified key by including its SHA1 hash in the KRL.
+.It Cm sha256 : Ar public_key
+Revokes the specified key by including its SHA256 hash in the KRL.
+KRLs that revoke keys by SHA256 hash are not supported by OpenSSH versions
+prior to 7.9.
+.It Cm hash : Ar fingerprint
+Revokes a key using by fingerprint hash, as obtained from a
+.Xr sshd 8
+authentication log message or the
+.Nm
+.Fl l
+flag.
+Only SHA256 fingerprints are supported here and resultant KRLs are
+not supported by OpenSSH versions prior to 7.9.
 .El
 .Pp
 KRLs may be updated using the
diff --git a/ssh-keygen.c b/ssh-keygen.c
index 22860ad9..748ce37d 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-keygen.c,v 1.319 2018/08/08 01:16:01 djm Exp $ */
+/* $OpenBSD: ssh-keygen.c,v 1.320 2018/09/12 01:21:34 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
  * Copyright (c) 1994 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -2079,6 +2079,41 @@ load_krl(const char *path, struct ssh_krl **krlp)
 	sshbuf_free(krlbuf);
 }
 
+static void
+hash_to_blob(const char *cp, u_char **blobp, size_t *lenp,
+    const char *file, u_long lnum)
+{
+	char *tmp;
+	size_t tlen;
+	struct sshbuf *b;
+	int r;
+
+	if (strncmp(cp, "SHA256:", 7) != 0)
+		fatal("%s:%lu: unsupported hash algorithm", file, lnum);
+	cp += 7;
+
+	/*
+	 * OpenSSH base64 hashes omit trailing '='
+	 * characters; put them back for decode.
+	 */
+	tlen = strlen(cp);
+	tmp = xmalloc(tlen + 4 + 1);
+	strlcpy(tmp, cp, tlen + 1);
+	while ((tlen % 4) != 0) {
+		tmp[tlen++] = '=';
+		tmp[tlen] = '\0';
+	}
+	if ((b = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new failed", __func__);
+	if ((r = sshbuf_b64tod(b, tmp)) != 0)
+		fatal("%s:%lu: decode hash failed: %s", file, lnum, ssh_err(r));
+	free(tmp);
+	*lenp = sshbuf_len(b);
+	*blobp = xmalloc(*lenp);
+	memcpy(*blobp, sshbuf_ptr(b), *lenp);
+	sshbuf_free(b);
+}
+
 static void
 update_krl_from_file(struct passwd *pw, const char *file, int wild_ca,
     const struct sshkey *ca, struct ssh_krl *krl)
@@ -2086,9 +2121,10 @@ update_krl_from_file(struct passwd *pw, const char *file, int wild_ca,
 	struct sshkey *key = NULL;
 	u_long lnum = 0;
 	char *path, *cp, *ep, *line = NULL;
-	size_t linesize = 0;
+	u_char *blob = NULL;
+	size_t blen = 0, linesize = 0;
 	unsigned long long serial, serial2;
-	int i, was_explicit_key, was_sha1, r;
+	int i, was_explicit_key, was_sha1, was_sha256, was_hash, r;
 	FILE *krl_spec;
 
 	path = tilde_expand_filename(file, pw->pw_uid);
@@ -2103,7 +2139,7 @@ update_krl_from_file(struct passwd *pw, const char *file, int wild_ca,
 		printf("Revoking from %s\n", path);
 	while (getline(&line, &linesize, krl_spec) != -1) {
 		lnum++;
-		was_explicit_key = was_sha1 = 0;
+		was_explicit_key = was_sha1 = was_sha256 = was_hash = 0;
 		cp = line + strspn(line, " \t");
 		/* Trim trailing space, comments and strip \n */
 		for (i = 0, r = -1; cp[i] != '\0'; i++) {
@@ -2168,6 +2204,11 @@ update_krl_from_file(struct passwd *pw, const char *file, int wild_ca,
 			cp = cp + strspn(cp, " \t");
 			if (ssh_krl_revoke_cert_by_key_id(krl, ca, cp) != 0)
 				fatal("%s: revoke key ID failed", __func__);
+		} else if (strncasecmp(cp, "hash:", 5) == 0) {
+			cp += 5;
+			cp = cp + strspn(cp, " \t");
+			hash_to_blob(cp, &blob, &blen, file, lnum);
+			r = ssh_krl_revoke_key_sha256(krl, blob, blen);
 		} else {
 			if (strncasecmp(cp, "key:", 4) == 0) {
 				cp += 4;
@@ -2177,7 +2218,10 @@ update_krl_from_file(struct passwd *pw, const char *file, int wild_ca,
 				cp += 5;
 				cp = cp + strspn(cp, " \t");
 				was_sha1 = 1;
-			} else {
+			} else if (strncasecmp(cp, "sha256:", 7) == 0) {
+				cp += 7;
+				cp = cp + strspn(cp, " \t");
+				was_sha256 = 1;
 				/*
 				 * Just try to process the line as a key.
 				 * Parsing will fail if it isn't.
@@ -2190,13 +2234,28 @@ update_krl_from_file(struct passwd *pw, const char *file, int wild_ca,
 				    path, lnum, ssh_err(r));
 			if (was_explicit_key)
 				r = ssh_krl_revoke_key_explicit(krl, key);
-			else if (was_sha1)
-				r = ssh_krl_revoke_key_sha1(krl, key);
-			else
+			else if (was_sha1) {
+				if (sshkey_fingerprint_raw(key,
+				    SSH_DIGEST_SHA1, &blob, &blen) != 0) {
+					fatal("%s:%lu: fingerprint failed",
+					    file, lnum);
+				}
+				r = ssh_krl_revoke_key_sha1(krl, blob, blen);
+			} else if (was_sha256) {
+				if (sshkey_fingerprint_raw(key,
+				    SSH_DIGEST_SHA256, &blob, &blen) != 0) {
+					fatal("%s:%lu: fingerprint failed",
+					    file, lnum);
+				}
+				r = ssh_krl_revoke_key_sha256(krl, blob, blen);
+			} else
 				r = ssh_krl_revoke_key(krl, key);
 			if (r != 0)
 				fatal("%s: revoke key failed: %s",
 				    __func__, ssh_err(r));
+			freezero(blob, blen);
+			blob = NULL;
+			blen = 0;
 			sshkey_free(key);
 		}
 	}

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


More information about the openssh-commits mailing list