OpenSSH 8.1 was released on 2019-10-09. It is available from the trunk
authorchristos <christos@NetBSD.org>
Sat, 12 Oct 2019 15:13:54 +0000
branchtrunk
changeset 455270 a0c08292c65b
parent 455262 40c6911d9234
child 455271 9ed269afacdb
OpenSSH 8.1 was released on 2019-10-09. It is available from the mirrors listed at https://www.openssh.com/. OpenSSH is a 100% complete SSH protocol 2.0 implementation and includes sftp client and server support. Once again, we would like to thank the OpenSSH community for their continued support of the project, especially those who contributed code or patches, reported bugs, tested snapshots or donated to the project. More information on donations may be found at: http://www.openssh.com/donations.html Security ======== * ssh(1), sshd(8), ssh-add(1), ssh-keygen(1): an exploitable integer overflow bug was found in the private key parsing code for the XMSS key type. This key type is still experimental and support for it is not compiled by default. No user-facing autoconf option exists in portable OpenSSH to enable it. This bug was found by Adam Zabrocki and reported via SecuriTeam's SSD program. * ssh(1), sshd(8), ssh-agent(1): add protection for private keys at rest in RAM against speculation and memory side-channel attacks like Spectre, Meltdown and Rambleed. This release encrypts private keys when they are not in use with a symmetric key that is derived from a relatively large "prekey" consisting of random data (currently 16KB). Potentially-incompatible changes ================================ This release includes a number of changes that may affect existing configurations: * ssh-keygen(1): when acting as a CA and signing certificates with an RSA key, default to using the rsa-sha2-512 signature algorithm. Certificates signed by RSA keys will therefore be incompatible with OpenSSH versions prior to 7.2 unless the default is overridden (using "ssh-keygen -t ssh-rsa -s ...").
crypto/external/bsd/openssh/dist/PROTOCOL.sshsig
crypto/external/bsd/openssh/dist/sftp-realpath.c
crypto/external/bsd/openssh/dist/sshsig.c
crypto/external/bsd/openssh/dist/sshsig.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crypto/external/bsd/openssh/dist/PROTOCOL.sshsig	Sat Oct 12 15:13:54 2019 +0000
@@ -0,0 +1,99 @@
+This document describes a lightweight SSH Signature format
+that is compatible with SSH keys and wire formats.
+
+At present, only detached and armored signatures are supported.
+
+1. Armored format
+
+The Armored SSH signatures consist of a header, a base64
+encoded blob, and a footer.
+
+The header is the string "-----BEGIN SSH SIGNATURE-----"
+followed by a newline. The footer is the string
+"-----END SSH SIGNATURE-----" immediately after a newline.
+
+The header MUST be present at the start of every signature.
+Files containing the signature MUST start with the header.
+Likewise, the footer MUST be present at the end of every
+signature.
+
+The base64 encoded blob SHOULD be broken up by newlines
+every 76 characters.
+
+Example:
+
+-----BEGIN SSH SIGNATURE-----
+U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgJKxoLBJBivUPNTUJUSslQTt2hD
+jozKvHarKeN8uYFqgAAAADZm9vAAAAAAAAAFMAAAALc3NoLWVkMjU1MTkAAABAKNC4IEbt
+Tq0Fb56xhtuE1/lK9H9RZJfON4o6hE9R4ZGFX98gy0+fFJ/1d2/RxnZky0Y7GojwrZkrHT
+FgCqVWAQ==
+-----END SSH SIGNATURE-----
+
+2. Blob format
+
+#define MAGIC_PREAMBLE "SSHSIG"
+#define SIG_VERSION    0x01
+
+        byte[6]   MAGIC_PREAMBLE
+        uint32    SIG_VERSION
+        string    publickey
+        string    namespace
+        string    reserved
+        string    hash_algorithm
+        string    signature
+
+The publickey field MUST contain the serialisation of the
+public key used to make the signature using the usual SSH
+encoding rules, i.e RFC4253, RFC5656,
+draft-ietf-curdle-ssh-ed25519-ed448, etc.
+
+Verifiers MUST reject signatures with versions greater than those
+they support.
+
+The purpose of the namespace value is to specify a unambiguous
+interpretation domain for the signature, e.g. file signing.
+This prevents cross-protocol attacks caused by signatures
+intended for one intended domain being accepted in another.
+The namespace value MUST NOT be the empty string.
+
+The reserved value is present to encode future information
+(e.g. tags) into the signature. Implementations should ignore
+the reserved field if it is not empty.
+
+Data to be signed is first hashed with the specified hash_algorithm.
+This is done to limit the amount of data presented to the signature
+operation, which may be of concern if the signing key is held in limited
+or slow hardware or on a remote ssh-agent. The supported hash algorithms
+are "sha256" and "sha512".
+
+The signature itself is made using the SSH signature algorithm and
+encoding rules for the chosen key type. For RSA signatures, the
+signature algorithm must be "rsa-sha2-512" or "rsa-sha2-256" (i.e.
+not the legacy RSA-SHA1 "ssh-rsa").
+
+This blob is encoded as a string using the RFC4243 encoding
+rules and base64 encoded to form the middle part of the
+armored signature.
+
+
+3. Signed Data, of which the signature goes into the blob above
+
+#define MAGIC_PREAMBLE "SSHSIG"
+
+        byte[6]   MAGIC_PREAMBLE
+        string    namespace
+        string    reserved
+        string    hash_algorithm
+        string    H(message)
+
+The preamble is the six-byte sequence "SSHSIG". It is included to
+ensure that manual signatures can never be confused with any message
+signed during SSH user or host authentication.
+
+The reserved value is present to encode future information
+(e.g. tags) into the signature. Implementations should ignore
+the reserved field if it is not empty.
+
+The data is concatenated and passed to the SSH signing
+function.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crypto/external/bsd/openssh/dist/sftp-realpath.c	Sat Oct 12 15:13:54 2019 +0000
@@ -0,0 +1,224 @@
+/*	$OpenBSD: sftp-realpath.c,v 1.1 2019/07/05 04:55:40 djm Exp $ */
+/*
+ * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+#ifndef SYMLOOP_MAX
+# define SYMLOOP_MAX 32
+#endif
+
+/* XXX rewrite sftp-server to use POSIX realpath and remove this hack */
+
+char *sftp_realpath(const char *path, char *resolved);
+
+/*
+ * char *realpath(const char *path, char resolved[PATH_MAX]);
+ *
+ * Find the real name of path, by removing all ".", ".." and symlink
+ * components.  Returns (resolved) on success, or (NULL) on failure,
+ * in which case the path which caused trouble is left in (resolved).
+ */
+char *
+sftp_realpath(const char *path, char *resolved)
+{
+	struct stat sb;
+	char *p, *q, *s;
+	size_t left_len, resolved_len;
+	unsigned symlinks;
+	int serrno, slen, mem_allocated;
+	char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
+
+	if (path[0] == '\0') {
+		errno = ENOENT;
+		return (NULL);
+	}
+
+	serrno = errno;
+
+	if (resolved == NULL) {
+		resolved = malloc(PATH_MAX);
+		if (resolved == NULL)
+			return (NULL);
+		mem_allocated = 1;
+	} else
+		mem_allocated = 0;
+
+	symlinks = 0;
+	if (path[0] == '/') {
+		resolved[0] = '/';
+		resolved[1] = '\0';
+		if (path[1] == '\0')
+			return (resolved);
+		resolved_len = 1;
+		left_len = strlcpy(left, path + 1, sizeof(left));
+	} else {
+		if (getcwd(resolved, PATH_MAX) == NULL) {
+			if (mem_allocated)
+				free(resolved);
+			else
+				strlcpy(resolved, ".", PATH_MAX);
+			return (NULL);
+		}
+		resolved_len = strlen(resolved);
+		left_len = strlcpy(left, path, sizeof(left));
+	}
+	if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
+		errno = ENAMETOOLONG;
+		goto err;
+	}
+
+	/*
+	 * Iterate over path components in `left'.
+	 */
+	while (left_len != 0) {
+		/*
+		 * Extract the next path component and adjust `left'
+		 * and its length.
+		 */
+		p = strchr(left, '/');
+		s = p ? p : left + left_len;
+		if (s - left >= (ptrdiff_t)sizeof(next_token)) {
+			errno = ENAMETOOLONG;
+			goto err;
+		}
+		memcpy(next_token, left, s - left);
+		next_token[s - left] = '\0';
+		left_len -= s - left;
+		if (p != NULL)
+			memmove(left, s + 1, left_len + 1);
+		if (resolved[resolved_len - 1] != '/') {
+			if (resolved_len + 1 >= PATH_MAX) {
+				errno = ENAMETOOLONG;
+				goto err;
+			}
+			resolved[resolved_len++] = '/';
+			resolved[resolved_len] = '\0';
+		}
+		if (next_token[0] == '\0')
+			continue;
+		else if (strcmp(next_token, ".") == 0)
+			continue;
+		else if (strcmp(next_token, "..") == 0) {
+			/*
+			 * Strip the last path component except when we have
+			 * single "/"
+			 */
+			if (resolved_len > 1) {
+				resolved[resolved_len - 1] = '\0';
+				q = strrchr(resolved, '/') + 1;
+				*q = '\0';
+				resolved_len = q - resolved;
+			}
+			continue;
+		}
+
+		/*
+		 * Append the next path component and lstat() it. If
+		 * lstat() fails we still can return successfully if
+		 * there are no more path components left.
+		 */
+		resolved_len = strlcat(resolved, next_token, PATH_MAX);
+		if (resolved_len >= PATH_MAX) {
+			errno = ENAMETOOLONG;
+			goto err;
+		}
+		if (lstat(resolved, &sb) != 0) {
+			if (errno == ENOENT && p == NULL) {
+				errno = serrno;
+				return (resolved);
+			}
+			goto err;
+		}
+		if (S_ISLNK(sb.st_mode)) {
+			if (symlinks++ > SYMLOOP_MAX) {
+				errno = ELOOP;
+				goto err;
+			}
+			slen = readlink(resolved, symlink, sizeof(symlink) - 1);
+			if (slen < 0)
+				goto err;
+			symlink[slen] = '\0';
+			if (symlink[0] == '/') {
+				resolved[1] = 0;
+				resolved_len = 1;
+			} else if (resolved_len > 1) {
+				/* Strip the last path component. */
+				resolved[resolved_len - 1] = '\0';
+				q = strrchr(resolved, '/') + 1;
+				*q = '\0';
+				resolved_len = q - resolved;
+			}
+
+			/*
+			 * If there are any path components left, then
+			 * append them to symlink. The result is placed
+			 * in `left'.
+			 */
+			if (p != NULL) {
+				if (symlink[slen - 1] != '/') {
+					if (slen + 1 >=
+					    (ptrdiff_t)sizeof(symlink)) {
+						errno = ENAMETOOLONG;
+						goto err;
+					}
+					symlink[slen] = '/';
+					symlink[slen + 1] = 0;
+				}
+				left_len = strlcat(symlink, left, sizeof(symlink));
+				if (left_len >= sizeof(symlink)) {
+					errno = ENAMETOOLONG;
+					goto err;
+				}
+			}
+			left_len = strlcpy(left, symlink, sizeof(left));
+		}
+	}
+
+	/*
+	 * Remove trailing slash except when the resolved pathname
+	 * is a single "/".
+	 */
+	if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
+		resolved[resolved_len - 1] = '\0';
+	return (resolved);
+
+err:
+	if (mem_allocated)
+		free(resolved);
+	return (NULL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crypto/external/bsd/openssh/dist/sshsig.c	Sat Oct 12 15:13:54 2019 +0000
@@ -0,0 +1,799 @@
+/*
+ * Copyright (c) 2019 Google LLC
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "authfd.h"
+#include "authfile.h"
+#include "log.h"
+#include "misc.h"
+#include "sshbuf.h"
+#include "sshsig.h"
+#include "ssherr.h"
+#include "sshkey.h"
+#include "match.h"
+#include "digest.h"
+
+#define SIG_VERSION		0x01
+#define MAGIC_PREAMBLE		"SSHSIG"
+#define MAGIC_PREAMBLE_LEN	(sizeof(MAGIC_PREAMBLE) - 1)
+#define BEGIN_SIGNATURE		"-----BEGIN SSH SIGNATURE-----\n"
+#define END_SIGNATURE		"-----END SSH SIGNATURE-----"
+#define RSA_SIGN_ALG		"rsa-sha2-512" /* XXX maybe make configurable */
+#define RSA_SIGN_ALLOWED	"rsa-sha2-512,rsa-sha2-256"
+#define HASHALG_DEFAULT		"sha512" /* XXX maybe make configurable */
+#define HASHALG_ALLOWED		"sha256,sha512"
+
+int
+sshsig_armor(const struct sshbuf *blob, struct sshbuf **out)
+{
+	struct sshbuf *buf = NULL;
+	int r = SSH_ERR_INTERNAL_ERROR;
+
+	*out = NULL;
+
+	if ((buf = sshbuf_new()) == NULL) {
+		error("%s: sshbuf_new failed", __func__);
+		r = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+
+	if ((r = sshbuf_put(buf, BEGIN_SIGNATURE,
+	    sizeof(BEGIN_SIGNATURE)-1)) != 0) {
+		error("%s: sshbuf_putf failed: %s", __func__, ssh_err(r));
+		goto out;
+	}
+
+	if ((r = sshbuf_dtob64(blob, buf, 1)) != 0) {
+		error("%s: Couldn't base64 encode signature blob: %s",
+		    __func__, ssh_err(r));
+		goto out;
+	}
+
+	if ((r = sshbuf_put(buf, END_SIGNATURE,
+	    sizeof(END_SIGNATURE)-1)) != 0 ||
+	    (r = sshbuf_put_u8(buf, '\n')) != 0) {
+		error("%s: sshbuf_put failed: %s", __func__, ssh_err(r));
+		goto out;
+	}
+	/* success */
+	*out = buf;
+	buf = NULL; /* transferred */
+	r = 0;
+ out:
+	sshbuf_free(buf);
+	return r;
+}
+
+int
+sshsig_dearmor(struct sshbuf *sig, struct sshbuf **out)
+{
+	int r;
+	size_t eoffset = 0;
+	struct sshbuf *buf = NULL;
+	struct sshbuf *sbuf = NULL;
+	char *b64 = NULL;
+
+	if ((sbuf = sshbuf_fromb(sig)) == NULL) {
+		error("%s: sshbuf_fromb failed", __func__);
+		return SSH_ERR_ALLOC_FAIL;
+	}
+
+	if ((r = sshbuf_cmp(sbuf, 0,
+	    BEGIN_SIGNATURE, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
+		error("Couldn't parse signature: missing header");
+		goto done;
+	}
+
+	if ((r = sshbuf_consume(sbuf, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
+		error("%s: sshbuf_consume failed: %s", __func__, ssh_err(r));
+		goto done;
+	}
+
+	if ((r = sshbuf_find(sbuf, 0, "\n" END_SIGNATURE,
+	    sizeof("\n" END_SIGNATURE)-1, &eoffset)) != 0) {
+		error("Couldn't parse signature: missing footer");
+		goto done;
+	}
+
+	if ((r = sshbuf_consume_end(sbuf, sshbuf_len(sbuf)-eoffset)) != 0) {
+		error("%s: sshbuf_consume failed: %s", __func__, ssh_err(r));
+		goto done;
+	}
+
+	if ((b64 = sshbuf_dup_string(sbuf)) == NULL) {
+		error("%s: sshbuf_dup_string failed", __func__);
+		r = SSH_ERR_ALLOC_FAIL;
+		goto done;
+	}
+
+	if ((buf = sshbuf_new()) == NULL) {
+		error("%s: sshbuf_new() failed", __func__);
+		r = SSH_ERR_ALLOC_FAIL;
+		goto done;
+	}
+
+	if ((r = sshbuf_b64tod(buf, b64)) != 0) {
+		error("Couldn't decode signature: %s", ssh_err(r));
+		goto done;
+	}
+
+	/* success */
+	*out = buf;
+	r = 0;
+	buf = NULL; /* transferred */
+done:
+	sshbuf_free(buf);
+	sshbuf_free(sbuf);
+	free(b64);
+	return r;
+}
+
+static int
+sshsig_wrap_sign(struct sshkey *key, const char *hashalg,
+    const struct sshbuf *h_message, const char *sig_namespace,
+    struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
+{
+	int r;
+	size_t slen = 0;
+	u_char *sig = NULL;
+	struct sshbuf *blob = NULL;
+	struct sshbuf *tosign = NULL;
+	const char *sign_alg = NULL;
+
+	if ((tosign = sshbuf_new()) == NULL ||
+	    (blob = sshbuf_new()) == NULL) {
+		error("%s: sshbuf_new failed", __func__);
+		r = SSH_ERR_ALLOC_FAIL;
+		goto done;
+	}
+
+	if ((r = sshbuf_put(tosign, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
+	    (r = sshbuf_put_cstring(tosign, sig_namespace)) != 0 ||
+	    (r = sshbuf_put_string(tosign, NULL, 0)) != 0 || /* reserved */
+	    (r = sshbuf_put_cstring(tosign, hashalg)) != 0 ||
+	    (r = sshbuf_put_stringb(tosign, h_message)) != 0) {
+		error("Couldn't construct message to sign: %s", ssh_err(r));
+		goto done;
+	}
+
+	/* If using RSA keys then default to a good signature algorithm */
+	if (sshkey_type_plain(key->type) == KEY_RSA)
+		sign_alg = RSA_SIGN_ALG;
+
+	if (signer != NULL) {
+		if ((r = signer(key, &sig, &slen,
+		    sshbuf_ptr(tosign), sshbuf_len(tosign),
+		    sign_alg, 0, signer_ctx)) != 0) {
+			error("Couldn't sign message: %s", ssh_err(r));
+			goto done;
+		}
+	} else {
+		if ((r = sshkey_sign(key, &sig, &slen,
+		    sshbuf_ptr(tosign), sshbuf_len(tosign),
+		    sign_alg, 0)) != 0) {
+			error("Couldn't sign message: %s", ssh_err(r));
+			goto done;
+		}
+	}
+
+	if ((r = sshbuf_put(blob, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
+	    (r = sshbuf_put_u32(blob, SIG_VERSION)) != 0 ||
+	    (r = sshkey_puts(key, blob)) != 0 ||
+	    (r = sshbuf_put_cstring(blob, sig_namespace)) != 0 ||
+	    (r = sshbuf_put_string(blob, NULL, 0)) != 0 || /* reserved */
+	    (r = sshbuf_put_cstring(blob, hashalg)) != 0 ||
+	    (r = sshbuf_put_string(blob, sig, slen)) != 0) {
+		error("Couldn't populate blob: %s", ssh_err(r));
+		goto done;
+	}
+
+	*out = blob;
+	blob = NULL;
+	r = 0;
+done:
+	free(sig);
+	sshbuf_free(blob);
+	sshbuf_free(tosign);
+	return r;
+}
+
+/* Check preamble and version. */
+static int
+sshsig_parse_preamble(struct sshbuf *buf)
+{
+	int r = SSH_ERR_INTERNAL_ERROR;
+	uint32_t sversion;
+
+	if ((r = sshbuf_cmp(buf, 0, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
+	    (r = sshbuf_consume(buf, (sizeof(MAGIC_PREAMBLE)-1))) != 0 ||
+	    (r = sshbuf_get_u32(buf, &sversion)) != 0) {
+		error("Couldn't verify signature: invalid format");
+		return r;
+	}
+
+	if (sversion > SIG_VERSION) {
+		error("Signature version %lu is larger than supported "
+		    "version %u", (unsigned long)sversion, SIG_VERSION);
+		return SSH_ERR_INVALID_FORMAT;
+	}
+	return 0;
+}
+
+static int
+sshsig_check_hashalg(const char *hashalg)
+{
+	if (hashalg == NULL ||
+	    match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1)
+		return 0;
+	error("%s: unsupported hash algorithm \"%.100s\"", __func__, hashalg);
+	return SSH_ERR_SIGN_ALG_UNSUPPORTED;
+}
+
+static int
+sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp)
+{
+	struct sshbuf *buf = NULL;
+	char *hashalg = NULL;
+	int r = SSH_ERR_INTERNAL_ERROR;
+
+	if (hashalgp != NULL)
+		*hashalgp = NULL;
+	if ((buf = sshbuf_fromb(signature)) == NULL)
+		return SSH_ERR_ALLOC_FAIL;
+	if ((r = sshsig_parse_preamble(buf)) != 0)
+		goto done;
+	if ((r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
+	    (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
+	    (r = sshbuf_get_string(buf, NULL, NULL)) != 0 ||
+	    (r = sshbuf_get_cstring(buf, &hashalg, NULL)) != 0 ||
+	    (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0) {
+		error("Couldn't parse signature blob: %s", ssh_err(r));
+		goto done;
+	}
+
+	/* success */
+	r = 0;
+	*hashalgp = hashalg;
+	hashalg = NULL;
+ done:
+	free(hashalg);
+	sshbuf_free(buf);
+	return r;
+}
+
+static int
+sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
+    const struct sshbuf *h_message, const char *expect_namespace,
+    struct sshkey **sign_keyp)
+{
+	int r = SSH_ERR_INTERNAL_ERROR;
+	struct sshbuf *buf = NULL, *toverify = NULL;
+	struct sshkey *key = NULL;
+	const u_char *sig;
+	char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL;
+	size_t siglen;
+
+	debug("%s: verify message length %zu", __func__, sshbuf_len(h_message));
+	if (sign_keyp != NULL)
+		*sign_keyp = NULL;
+
+	if ((toverify = sshbuf_new()) == NULL) {
+		error("%s: sshbuf_new failed", __func__);
+		r = SSH_ERR_ALLOC_FAIL;
+		goto done;
+	}
+	if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE,
+	    MAGIC_PREAMBLE_LEN)) != 0 ||
+	    (r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 ||
+	    (r = sshbuf_put_string(toverify, NULL, 0)) != 0 || /* reserved */
+	    (r = sshbuf_put_cstring(toverify, hashalg)) != 0 ||
+	    (r = sshbuf_put_stringb(toverify, h_message)) != 0) {
+		error("Couldn't construct message to verify: %s", ssh_err(r));
+		goto done;
+	}
+
+	if ((r = sshsig_parse_preamble(signature)) != 0)
+		goto done;
+
+	if ((r = sshkey_froms(signature, &key)) != 0 ||
+	    (r = sshbuf_get_cstring(signature, &got_namespace, NULL)) != 0 ||
+	    (r = sshbuf_get_string(signature, NULL, NULL)) != 0 ||
+	    (r = sshbuf_get_cstring(signature, &sig_hashalg, NULL)) != 0 ||
+	    (r = sshbuf_get_string_direct(signature, &sig, &siglen)) != 0) {
+		error("Couldn't parse signature blob: %s", ssh_err(r));
+		goto done;
+	}
+
+	if (sshbuf_len(signature) != 0) {
+		error("Signature contains trailing data");
+		r = SSH_ERR_INVALID_FORMAT;
+		goto done;
+	}
+
+	if (strcmp(expect_namespace, got_namespace) != 0) {
+		error("Couldn't verify signature: namespace does not match");
+		debug("%s: expected namespace \"%s\" received \"%s\"",
+		    __func__, expect_namespace, got_namespace);
+		r = SSH_ERR_SIGNATURE_INVALID;
+		goto done;
+	}
+	if (strcmp(hashalg, sig_hashalg) != 0) {
+		error("Couldn't verify signature: hash algorithm mismatch");
+		debug("%s: expected algorithm \"%s\" received \"%s\"",
+		    __func__, hashalg, sig_hashalg);
+		r = SSH_ERR_SIGNATURE_INVALID;
+		goto done;
+	}
+	/* Ensure that RSA keys use an acceptable signature algorithm */
+	if (sshkey_type_plain(key->type) == KEY_RSA) {
+		if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) {
+			error("Couldn't verify signature: unable to get "
+			    "signature type: %s", ssh_err(r));
+			goto done;
+		}
+		if (match_pattern_list(sigtype, RSA_SIGN_ALLOWED, 0) != 1) {
+			error("Couldn't verify signature: unsupported RSA "
+			    "signature algorithm %s", sigtype);
+			r = SSH_ERR_SIGN_ALG_UNSUPPORTED;
+			goto done;
+		}
+	}
+	if ((r = sshkey_verify(key, sig, siglen, sshbuf_ptr(toverify),
+	    sshbuf_len(toverify), NULL, 0)) != 0) {
+		error("Signature verification failed: %s", ssh_err(r));
+		goto done;
+	}
+
+	/* success */
+	r = 0;
+	if (sign_keyp != NULL) {
+		*sign_keyp = key;
+		key = NULL; /* transferred */
+	}
+done:
+	free(got_namespace);
+	free(sigtype);
+	free(sig_hashalg);
+	sshbuf_free(buf);
+	sshbuf_free(toverify);
+	sshkey_free(key);
+	return r;
+}
+
+static int
+hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp)
+{
+	char *hex, hash[SSH_DIGEST_MAX_LENGTH];
+	int alg, r = SSH_ERR_INTERNAL_ERROR;
+	struct sshbuf *b = NULL;
+
+	*bp = NULL;
+	memset(hash, 0, sizeof(hash));
+
+	if ((r = sshsig_check_hashalg(hashalg)) != 0)
+		return r;
+	if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
+		error("%s: can't look up hash algorithm %s",
+		    __func__, hashalg);
+		return SSH_ERR_INTERNAL_ERROR;
+	}
+	if ((r = ssh_digest_buffer(alg, m, hash, sizeof(hash))) != 0) {
+		error("%s: ssh_digest_buffer failed: %s", __func__, ssh_err(r));
+		return r;
+	}
+	if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
+		debug3("%s: final hash: %s", __func__, hex);
+		freezero(hex, strlen(hex));
+	}
+	if ((b = sshbuf_new()) == NULL) {
+		r = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+	if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
+		error("%s: sshbuf_put: %s", __func__, ssh_err(r));
+		goto out;
+	}
+	*bp = b;
+	b = NULL; /* transferred */
+	/* success */
+	r = 0;
+ out:
+	sshbuf_free(b);
+	explicit_bzero(hash, sizeof(hash));
+	return 0;
+}
+
+int
+sshsig_signb(struct sshkey *key, const char *hashalg,
+    const struct sshbuf *message, const char *sig_namespace,
+    struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
+{
+	struct sshbuf *b = NULL;
+	int r = SSH_ERR_INTERNAL_ERROR;
+
+	if (hashalg == NULL)
+		hashalg = HASHALG_DEFAULT;
+	if (out != NULL)
+		*out = NULL;
+	if ((r = hash_buffer(message, hashalg, &b)) != 0) {
+		error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
+		goto out;
+	}
+	if ((r = sshsig_wrap_sign(key, hashalg, b, sig_namespace, out,
+	    signer, signer_ctx)) != 0)
+		goto out;
+	/* success */
+	r = 0;
+ out:
+	sshbuf_free(b);
+	return r;
+}
+
+int
+sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message,
+    const char *expect_namespace, struct sshkey **sign_keyp)
+{
+	struct sshbuf *b = NULL;
+	int r = SSH_ERR_INTERNAL_ERROR;
+	char *hashalg = NULL;
+
+	if (sign_keyp != NULL)
+		*sign_keyp = NULL;
+
+	if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
+		return r;
+	debug("%s: signature made with hash \"%s\"", __func__, hashalg);
+	if ((r = hash_buffer(message, hashalg, &b)) != 0) {
+		error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
+		goto out;
+	}
+	if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
+	    sign_keyp)) != 0)
+		goto out;
+	/* success */
+	r = 0;
+ out:
+	sshbuf_free(b);
+	free(hashalg);
+	return r;
+}
+
+static int
+hash_file(int fd, const char *hashalg, struct sshbuf **bp)
+{
+	char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH];
+	ssize_t n, total = 0;
+	struct ssh_digest_ctx *ctx;
+	int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR;
+	struct sshbuf *b = NULL;
+
+	*bp = NULL;
+	memset(hash, 0, sizeof(hash));
+
+	if ((r = sshsig_check_hashalg(hashalg)) != 0)
+		return r;
+	if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
+		error("%s: can't look up hash algorithm %s",
+		    __func__, hashalg);
+		return SSH_ERR_INTERNAL_ERROR;
+	}
+	if ((ctx = ssh_digest_start(alg)) == NULL) {
+		error("%s: ssh_digest_start failed", __func__);
+		return SSH_ERR_INTERNAL_ERROR;
+	}
+	for (;;) {
+		if ((n = read(fd, rbuf, sizeof(rbuf))) == -1) {
+			if (errno == EINTR || errno == EAGAIN)
+				continue;
+			oerrno = errno;
+			error("%s: read: %s", __func__, strerror(errno));
+			ssh_digest_free(ctx);
+			errno = oerrno;
+			r = SSH_ERR_SYSTEM_ERROR;
+			goto out;
+		} else if (n == 0) {
+			debug2("%s: hashed %zu bytes", __func__, total);
+			break; /* EOF */
+		}
+		total += (size_t)n;
+		if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) {
+			error("%s: ssh_digest_update: %s",
+			    __func__, ssh_err(r));
+			goto out;
+		}
+	}
+	if ((r = ssh_digest_final(ctx, hash, sizeof(hash))) != 0) {
+		error("%s: ssh_digest_final: %s", __func__, ssh_err(r));
+		goto out;
+	}
+	if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
+		debug3("%s: final hash: %s", __func__, hex);
+		freezero(hex, strlen(hex));
+	}
+	if ((b = sshbuf_new()) == NULL) {
+		r = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+	if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
+		error("%s: sshbuf_put: %s", __func__, ssh_err(r));
+		goto out;
+	}
+	*bp = b;
+	b = NULL; /* transferred */
+	/* success */
+	r = 0;
+ out:
+	sshbuf_free(b);
+	ssh_digest_free(ctx);
+	explicit_bzero(hash, sizeof(hash));
+	return 0;
+}
+
+int
+sshsig_sign_fd(struct sshkey *key, const char *hashalg,
+    int fd, const char *sig_namespace, struct sshbuf **out,
+    sshsig_signer *signer, void *signer_ctx)
+{
+	struct sshbuf *b = NULL;
+	int r = SSH_ERR_INTERNAL_ERROR;
+
+	if (hashalg == NULL)
+		hashalg = HASHALG_DEFAULT;
+	if (out != NULL)
+		*out = NULL;
+	if ((r = hash_file(fd, hashalg, &b)) != 0) {
+		error("%s: hash_file failed: %s", __func__, ssh_err(r));
+		return r;
+	}
+	if ((r = sshsig_wrap_sign(key, hashalg, b, sig_namespace, out,
+	    signer, signer_ctx)) != 0)
+		goto out;
+	/* success */
+	r = 0;
+ out:
+	sshbuf_free(b);
+	return r;
+}
+
+int
+sshsig_verify_fd(struct sshbuf *signature, int fd,
+    const char *expect_namespace, struct sshkey **sign_keyp)
+{
+	struct sshbuf *b = NULL;
+	int r = SSH_ERR_INTERNAL_ERROR;
+	char *hashalg = NULL;
+
+	if (sign_keyp != NULL)
+		*sign_keyp = NULL;
+
+	if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
+		return r;
+	debug("%s: signature made with hash \"%s\"", __func__, hashalg);
+	if ((r = hash_file(fd, hashalg, &b)) != 0) {
+		error("%s: hash_file failed: %s", __func__, ssh_err(r));
+		goto out;
+	}
+	if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
+	    sign_keyp)) != 0)
+		goto out;
+	/* success */
+	r = 0;
+ out:
+	sshbuf_free(b);
+	free(hashalg);
+	return r;
+}
+
+struct sshsigopt {
+	int ca;
+	char *namespaces;
+};
+
+struct sshsigopt *
+sshsigopt_parse(const char *opts, const char *path, u_long linenum,
+    const char **errstrp)
+{
+	struct sshsigopt *ret;
+	int r;
+	const char *errstr = NULL;
+
+	if ((ret = calloc(1, sizeof(*ret))) == NULL)
+		return NULL;
+	if (opts == NULL || *opts == '\0')
+		return ret; /* Empty options yields empty options :) */
+
+	while (*opts && *opts != ' ' && *opts != '\t') {
+		/* flag options */
+		if ((r = opt_flag("cert-authority", 0, &opts)) != -1) {
+			ret->ca = 1;
+		} else if (opt_match(&opts, "namespaces")) {
+			if (ret->namespaces != NULL) {
+				errstr = "multiple \"namespaces\" clauses";
+				goto fail;
+			}
+			ret->namespaces = opt_dequote(&opts, &errstr);
+			if (ret->namespaces == NULL)
+				goto fail;
+		}
+		/*
+		 * Skip the comma, and move to the next option
+		 * (or break out if there are no more).
+		 */
+		if (*opts == '\0' || *opts == ' ' || *opts == '\t')
+			break;		/* End of options. */
+		/* Anything other than a comma is an unknown option */
+		if (*opts != ',') {
+			errstr = "unknown key option";
+			goto fail;
+		}
+		opts++;
+		if (*opts == '\0') {
+			errstr = "unexpected end-of-options";
+			goto fail;
+		}
+	}
+	/* success */
+	return ret;
+ fail:
+	if (errstrp != NULL)
+		*errstrp = errstr;
+	sshsigopt_free(ret);
+	return NULL;
+}
+
+void
+sshsigopt_free(struct sshsigopt *opts)
+{
+	if (opts == NULL)
+		return;
+	free(opts->namespaces);
+	free(opts);
+}
+
+static int
+check_allowed_keys_line(const char *path, u_long linenum, char *line,
+    const struct sshkey *sign_key, const char *principal,
+    const char *sig_namespace)
+{
+	struct sshkey *found_key = NULL;
+	char *cp, *opts = NULL, *identities = NULL;
+	int r, found = 0;
+	const char *reason = NULL;
+	struct sshsigopt *sigopts = NULL;
+
+	if ((found_key = sshkey_new(KEY_UNSPEC)) == NULL) {
+		error("%s: sshkey_new failed", __func__);
+		return SSH_ERR_ALLOC_FAIL;
+	}
+
+	/* format: identity[,identity...] [option[,option...]] key */
+	cp = line;
+	cp = cp + strspn(cp, " \t"); /* skip leading whitespace */
+	if (*cp == '#' || *cp == '\0')
+		goto done;
+	if ((identities = strdelimw(&cp)) == NULL) {
+		error("%s:%lu: invalid line", path, linenum);
+		goto done;
+	}
+	if (match_pattern_list(principal, identities, 0) != 1) {
+		/* principal didn't match */
+		goto done;
+	}
+	debug("%s: %s:%lu: matched principal \"%s\"",
+	    __func__, path, linenum, principal);
+
+	if (sshkey_read(found_key, &cp) != 0) {
+		/* no key? Check for options */
+		opts = cp;
+		if (sshkey_advance_past_options(&cp) != 0) {
+			error("%s:%lu: invalid options",
+			    path, linenum);
+			goto done;
+		}
+		*cp++ = '\0';
+		skip_space(&cp);
+		if (sshkey_read(found_key, &cp) != 0) {
+			error("%s:%lu: invalid key", path,
+			    linenum);
+			goto done;
+		}
+	}
+	debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts);
+	if ((sigopts = sshsigopt_parse(opts, path, linenum, &reason)) == NULL) {
+		error("%s:%lu: bad options: %s", path, linenum, reason);
+		goto done;
+	}
+
+	/* Check whether options preclude the use of this key */
+	if (sigopts->namespaces != NULL &&
+	    match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) {
+		error("%s:%lu: key is not permitted for use in signature "
+		    "namespace \"%s\"", path, linenum, sig_namespace);
+		goto done;
+	}
+
+	if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
+		/* Exact match of key */
+		debug("%s:%lu: matched key and principal", path, linenum);
+		/* success */
+		found = 1;
+	} else if (sigopts->ca && sshkey_is_cert(sign_key) &&
+	    sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
+		/* Match of certificate's CA key */
+		if ((r = sshkey_cert_check_authority(sign_key, 0, 1,
+		    principal, &reason)) != 0) {
+			error("%s:%lu: certificate not authorized: %s",
+			    path, linenum, reason);
+			goto done;
+		}
+		debug("%s:%lu: matched certificate CA key", path, linenum);
+		/* success */
+		found = 1;
+	} else {
+		/* Principal matched but key didn't */
+		goto done;
+	}
+ done:
+	sshkey_free(found_key);
+	sshsigopt_free(sigopts);
+	return found ? 0 : SSH_ERR_KEY_NOT_FOUND;
+}
+
+int
+sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
+    const char *principal, const char *sig_namespace)
+{
+	FILE *f = NULL;
+	char *line = NULL;
+	size_t linesize = 0;
+	u_long linenum = 0;
+	int r, oerrno;
+
+	/* Check key and principal against file */
+	if ((f = fopen(path, "r")) == NULL) {
+		oerrno = errno;
+		error("Unable to open allowed keys file \"%s\": %s",
+		    path, strerror(errno));
+		errno = oerrno;
+		return SSH_ERR_SYSTEM_ERROR;
+	}
+
+	while (getline(&line, &linesize, f) != -1) {
+		linenum++;
+		r = check_allowed_keys_line(path, linenum, line, sign_key,
+		    principal, sig_namespace);
+		free(line);
+		line = NULL;
+		if (r == SSH_ERR_KEY_NOT_FOUND)
+			continue;
+		else if (r == 0) {
+			/* success */
+			fclose(f);
+			return 0;
+		} else
+			break;
+	}
+	/* Either we hit an error parsing or we simply didn't find the key */
+	fclose(f);
+	free(line);
+	return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crypto/external/bsd/openssh/dist/sshsig.h	Sat Oct 12 15:13:54 2019 +0000
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2019 Google LLC
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef SSHSIG_H
+#define SSHSIG_H
+
+struct sshbuf;
+struct sshkey;
+struct sshsigopt;
+
+typedef int sshsig_signer(struct sshkey *, u_char **, size_t *,
+    const u_char *, size_t, const char *, u_int, void *);
+
+/* Buffer-oriented API */
+
+/*
+ * Creates a detached SSH signature for a given buffer.
+ * Returns 0 on success or a negative SSH_ERR_* error code on failure.
+ * out is populated with the detached signature, or NULL on failure.
+ */
+int sshsig_signb(struct sshkey *key, const char *hashalg,
+    const struct sshbuf *message, const char *sig_namespace,
+    struct sshbuf **out, sshsig_signer *signer, void *signer_ctx);
+
+/*
+ * Verifies that a detached signature is valid and optionally returns key
+ * used to sign via argument.
+ * Returns 0 on success or a negative SSH_ERR_* error code on failure.
+ */
+int sshsig_verifyb(struct sshbuf *signature,
+    const struct sshbuf *message, const char *sig_namespace,
+    struct sshkey **sign_keyp);
+
+/* File/FD-oriented API */
+
+/*
+ * Creates a detached SSH signature for a given file.
+ * Returns 0 on success or a negative SSH_ERR_* error code on failure.
+ * out is populated with the detached signature, or NULL on failure.
+ */
+int sshsig_sign_fd(struct sshkey *key, const char *hashalg,
+    int fd, const char *sig_namespace, struct sshbuf **out,
+    sshsig_signer *signer, void *signer_ctx);
+
+/*
+ * Verifies that a detached signature over a file is valid and optionally
+ * returns key used to sign via argument.
+ * Returns 0 on success or a negative SSH_ERR_* error code on failure.
+ */
+int sshsig_verify_fd(struct sshbuf *signature, int fd,
+    const char *sig_namespace, struct sshkey **sign_keyp);
+
+/* Utility functions */
+
+/*
+ * Return a base64 encoded "ASCII armoured" version of a raw signature.
+ */
+int sshsig_armor(const struct sshbuf *blob, struct sshbuf **out);
+
+/*
+ * Decode a base64 encoded armoured signature to a raw signature.
+ */
+int sshsig_dearmor(struct sshbuf *sig, struct sshbuf **out);
+
+/*
+ * Checks whether a particular key/principal/namespace is permitted by
+ * an allowed_keys file. Returns 0 on success.
+ */
+int sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
+    const char *principal, const char *ns);
+
+/* Parse zero or more allowed_keys signature options */
+struct sshsigopt *sshsigopt_parse(const char *opts,
+    const char *path, u_long linenum, const char **errstrp);
+
+/* Free signature options */
+void sshsigopt_free(struct sshsigopt *opts);
+
+#endif /* SSHSIG_H */