upstream: sshsig tweaks and improvements from and suggested by

Markus

ok markus/me

OpenBSD-Commit-ID: ea4f46ad5a16b27af96e08c4877423918c4253e9
This commit is contained in:
djm@openbsd.org 2019-09-03 08:35:27 +00:00 committed by Damien Miller
parent 2a9c9f7272
commit d637c4aee6
5 changed files with 122 additions and 104 deletions

View File

@ -8,9 +8,9 @@ At present, only detached and armored signatures are supported.
The Armored SSH signatures consist of a header, a base64 The Armored SSH signatures consist of a header, a base64
encoded blob, and a footer. encoded blob, and a footer.
The header is the string “-----BEGIN SSH SIGNATURE-----” The header is the string "-----BEGIN SSH SIGNATURE-----"
followed by a newline. The footer is the string followed by a newline. The footer is the string
“-----END SSH SIGNATURE-----” immediately after a newline. "-----END SSH SIGNATURE-----" immediately after a newline.
The header MUST be present at the start of every signature. The header MUST be present at the start of every signature.
Files containing the signature MUST start with the header. Files containing the signature MUST start with the header.

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: ssh-keygen.1,v 1.163 2019/09/03 08:34:19 djm Exp $ .\" $OpenBSD: ssh-keygen.1,v 1.164 2019/09/03 08:35:27 djm Exp $
.\" .\"
.\" Author: Tatu Ylonen <ylo@cs.hut.fi> .\" Author: Tatu Ylonen <ylo@cs.hut.fi>
.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -986,7 +986,7 @@ Indicates that this key is accepted as a certificate authority (CA) and
that certificates signed by this CA may be accepted for verification. that certificates signed by this CA may be accepted for verification.
.It Cm namespaces="namespace-list" .It Cm namespaces="namespace-list"
Specifies a pattern-list of namespaces that are accepted for this key. Specifies a pattern-list of namespaces that are accepted for this key.
If this option is present, the the signature namespace embedded in the If this option is present, the signature namespace embedded in the
signature object and presented on the verification command-line must signature object and presented on the verification command-line must
match the specified list before the key will be considered acceptable. match the specified list before the key will be considered acceptable.
.El .El

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keygen.c,v 1.344 2019/09/03 08:34:19 djm Exp $ */ /* $OpenBSD: ssh-keygen.c,v 1.345 2019/09/03 08:35:27 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland

180
sshsig.c
View File

@ -230,7 +230,7 @@ sshsig_parse_preamble(struct sshbuf *buf)
return r; return r;
} }
if (sversion < SIG_VERSION) { if (sversion > SIG_VERSION) {
error("Signature version %lu is larger than supported " error("Signature version %lu is larger than supported "
"version %u", (unsigned long)sversion, SIG_VERSION); "version %u", (unsigned long)sversion, SIG_VERSION);
return SSH_ERR_INVALID_FORMAT; return SSH_ERR_INVALID_FORMAT;
@ -241,7 +241,8 @@ sshsig_parse_preamble(struct sshbuf *buf)
static int static int
sshsig_check_hashalg(const char *hashalg) sshsig_check_hashalg(const char *hashalg)
{ {
if (match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1) if (hashalg == NULL ||
match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1)
return 0; return 0;
error("%s: unsupported hash algorithm \"%.100s\"", __func__, hashalg); error("%s: unsupported hash algorithm \"%.100s\"", __func__, hashalg);
return SSH_ERR_SIGN_ALG_UNSUPPORTED; return SSH_ERR_SIGN_ALG_UNSUPPORTED;
@ -268,8 +269,6 @@ sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp)
error("Couldn't parse signature blob: %s", ssh_err(r)); error("Couldn't parse signature blob: %s", ssh_err(r));
goto done; goto done;
} }
if ((r = sshsig_check_hashalg(hashalg)) != 0)
goto done;
/* success */ /* success */
r = 0; r = 0;
@ -293,6 +292,7 @@ sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL; char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL;
size_t siglen; size_t siglen;
debug("%s: verify message length %zu", __func__, sshbuf_len(h_message));
if (sign_keyp != NULL) if (sign_keyp != NULL)
*sign_keyp = NULL; *sign_keyp = NULL;
@ -301,9 +301,6 @@ sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
r = SSH_ERR_ALLOC_FAIL; r = SSH_ERR_ALLOC_FAIL;
goto done; goto done;
} }
if ((r = sshsig_check_hashalg(hashalg)) != 0)
goto done;
if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE, if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE,
MAGIC_PREAMBLE_LEN)) != 0 || MAGIC_PREAMBLE_LEN)) != 0 ||
(r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 || (r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 ||
@ -382,36 +379,65 @@ done:
return r; return r;
} }
int static int
sshsig_sign_message(struct sshkey *key, const char *hashalg, hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp)
const struct sshbuf *message, const char *sig_namespace,
struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
{ {
u_char hash[SSH_DIGEST_MAX_LENGTH]; char *hex, hash[SSH_DIGEST_MAX_LENGTH];
struct sshbuf *b = NULL;
int alg, r = SSH_ERR_INTERNAL_ERROR; int alg, r = SSH_ERR_INTERNAL_ERROR;
struct sshbuf *b = NULL;
if (out != NULL) *bp = NULL;
*out = NULL; memset(hash, 0, sizeof(hash));
if (hashalg == NULL)
hashalg = HASHALG_DEFAULT;
if ((r = sshsig_check_hashalg(hashalg)) != 0) if ((r = sshsig_check_hashalg(hashalg)) != 0)
return r; return r;
if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) { if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
error("%s: can't look up hash algorithm %s", error("%s: can't look up hash algorithm %s",
__func__, HASHALG_DEFAULT); __func__, hashalg);
return SSH_ERR_INTERNAL_ERROR; return SSH_ERR_INTERNAL_ERROR;
} }
if ((r = ssh_digest_buffer(alg, message, hash, sizeof(hash))) != 0) { if ((r = ssh_digest_buffer(alg, m, hash, sizeof(hash))) != 0) {
error("%s: ssh_digest_buffer failed: %s", __func__, ssh_err(r)); error("%s: ssh_digest_buffer failed: %s", __func__, ssh_err(r));
return r; return r;
} }
if ((b = sshbuf_from(hash, ssh_digest_bytes(alg))) == NULL) { if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
error("%s: sshbuf_from failed", __func__); debug3("%s: final hash: %s", __func__, hex);
freezero(hex, strlen(hex));
}
if ((b = sshbuf_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL; r = SSH_ERR_ALLOC_FAIL;
goto out; 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, if ((r = sshsig_wrap_sign(key, hashalg, b, sig_namespace, out,
signer, signer_ctx)) != 0) signer, signer_ctx)) != 0)
goto out; goto out;
@ -419,17 +445,15 @@ sshsig_sign_message(struct sshkey *key, const char *hashalg,
r = 0; r = 0;
out: out:
sshbuf_free(b); sshbuf_free(b);
explicit_bzero(hash, sizeof(hash));
return r; return r;
} }
int int
sshsig_verify_message(struct sshbuf *signature, const struct sshbuf *message, sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message,
const char *expect_namespace, struct sshkey **sign_keyp) const char *expect_namespace, struct sshkey **sign_keyp)
{ {
u_char hash[SSH_DIGEST_MAX_LENGTH];
struct sshbuf *b = NULL; struct sshbuf *b = NULL;
int alg, r = SSH_ERR_INTERNAL_ERROR; int r = SSH_ERR_INTERNAL_ERROR;
char *hashalg = NULL; char *hashalg = NULL;
if (sign_keyp != NULL) if (sign_keyp != NULL)
@ -437,18 +461,9 @@ sshsig_verify_message(struct sshbuf *signature, const struct sshbuf *message,
if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0) if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
return r; return r;
if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) { debug("%s: signature made with hash \"%s\"", __func__, hashalg);
error("%s: can't look up hash algorithm %s", if ((r = hash_buffer(message, hashalg, &b)) != 0) {
__func__, HASHALG_DEFAULT); error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
return SSH_ERR_INTERNAL_ERROR;
}
if ((r = ssh_digest_buffer(alg, message, hash, sizeof(hash))) != 0) {
error("%s: ssh_digest_buffer failed: %s", __func__, ssh_err(r));
goto out;
}
if ((b = sshbuf_from(hash, ssh_digest_bytes(alg))) == NULL) {
error("%s: sshbuf_from failed", __func__);
r = SSH_ERR_ALLOC_FAIL;
goto out; goto out;
} }
if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace, if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
@ -459,20 +474,29 @@ sshsig_verify_message(struct sshbuf *signature, const struct sshbuf *message,
out: out:
sshbuf_free(b); sshbuf_free(b);
free(hashalg); free(hashalg);
explicit_bzero(hash, sizeof(hash));
return r; return r;
} }
static int static int
hash_file(int fd, int hashalg, u_char *hash, size_t hashlen) hash_file(int fd, const char *hashalg, struct sshbuf **bp)
{ {
char *hex, rbuf[8192]; char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH];
ssize_t n, total = 0; ssize_t n, total = 0;
struct ssh_digest_ctx *ctx; struct ssh_digest_ctx *ctx;
int r, oerrno; int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR;
struct sshbuf *b = NULL;
memset(hash, 0, hashlen); *bp = NULL;
if ((ctx = ssh_digest_start(hashalg)) == 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__); error("%s: ssh_digest_start failed", __func__);
return SSH_ERR_INTERNAL_ERROR; return SSH_ERR_INTERNAL_ERROR;
} }
@ -484,7 +508,8 @@ hash_file(int fd, int hashalg, u_char *hash, size_t hashlen)
error("%s: read: %s", __func__, strerror(errno)); error("%s: read: %s", __func__, strerror(errno));
ssh_digest_free(ctx); ssh_digest_free(ctx);
errno = oerrno; errno = oerrno;
return SSH_ERR_SYSTEM_ERROR; r = SSH_ERR_SYSTEM_ERROR;
goto out;
} else if (n == 0) { } else if (n == 0) {
debug2("%s: hashed %zu bytes", __func__, total); debug2("%s: hashed %zu bytes", __func__, total);
break; /* EOF */ break; /* EOF */
@ -493,20 +518,33 @@ hash_file(int fd, int hashalg, u_char *hash, size_t hashlen)
if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) { if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) {
error("%s: ssh_digest_update: %s", error("%s: ssh_digest_update: %s",
__func__, ssh_err(r)); __func__, ssh_err(r));
ssh_digest_free(ctx); goto out;
return r;
} }
} }
if ((r = ssh_digest_final(ctx, hash, hashlen)) != 0) { if ((r = ssh_digest_final(ctx, hash, sizeof(hash))) != 0) {
error("%s: ssh_digest_final: %s", __func__, ssh_err(r)); error("%s: ssh_digest_final: %s", __func__, ssh_err(r));
ssh_digest_free(ctx); goto out;
} }
if ((hex = tohex(hash, hashlen)) != NULL) { if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
debug3("%s: final hash: %s", __func__, hex); debug3("%s: final hash: %s", __func__, hex);
freezero(hex, strlen(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 */ /* success */
r = 0;
out:
sshbuf_free(b);
ssh_digest_free(ctx); ssh_digest_free(ctx);
explicit_bzero(hash, sizeof(hash));
return 0; return 0;
} }
@ -515,31 +553,17 @@ sshsig_sign_fd(struct sshkey *key, const char *hashalg,
int fd, const char *sig_namespace, struct sshbuf **out, int fd, const char *sig_namespace, struct sshbuf **out,
sshsig_signer *signer, void *signer_ctx) sshsig_signer *signer, void *signer_ctx)
{ {
u_char hash[SSH_DIGEST_MAX_LENGTH];
struct sshbuf *b = NULL; struct sshbuf *b = NULL;
int alg, r = SSH_ERR_INTERNAL_ERROR; int r = SSH_ERR_INTERNAL_ERROR;
if (out != NULL)
*out = NULL;
if (hashalg == NULL) if (hashalg == NULL)
hashalg = HASHALG_DEFAULT; hashalg = HASHALG_DEFAULT;
if (out != NULL)
if ((r = sshsig_check_hashalg(hashalg)) != 0) *out = NULL;
return r; if ((r = hash_file(fd, hashalg, &b)) != 0) {
if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
error("%s: can't look up hash algorithm %s",
__func__, HASHALG_DEFAULT);
return SSH_ERR_INTERNAL_ERROR;
}
if ((r = hash_file(fd, alg, hash, sizeof(hash))) != 0) {
error("%s: hash_file failed: %s", __func__, ssh_err(r)); error("%s: hash_file failed: %s", __func__, ssh_err(r));
return r; return r;
} }
if ((b = sshbuf_from(hash, ssh_digest_bytes(alg))) == NULL) {
error("%s: sshbuf_from failed", __func__);
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if ((r = sshsig_wrap_sign(key, hashalg, b, sig_namespace, out, if ((r = sshsig_wrap_sign(key, hashalg, b, sig_namespace, out,
signer, signer_ctx)) != 0) signer, signer_ctx)) != 0)
goto out; goto out;
@ -547,7 +571,6 @@ sshsig_sign_fd(struct sshkey *key, const char *hashalg,
r = 0; r = 0;
out: out:
sshbuf_free(b); sshbuf_free(b);
explicit_bzero(hash, sizeof(hash));
return r; return r;
} }
@ -555,9 +578,8 @@ int
sshsig_verify_fd(struct sshbuf *signature, int fd, sshsig_verify_fd(struct sshbuf *signature, int fd,
const char *expect_namespace, struct sshkey **sign_keyp) const char *expect_namespace, struct sshkey **sign_keyp)
{ {
u_char hash[SSH_DIGEST_MAX_LENGTH];
struct sshbuf *b = NULL; struct sshbuf *b = NULL;
int alg, r = SSH_ERR_INTERNAL_ERROR; int r = SSH_ERR_INTERNAL_ERROR;
char *hashalg = NULL; char *hashalg = NULL;
if (sign_keyp != NULL) if (sign_keyp != NULL)
@ -565,18 +587,9 @@ sshsig_verify_fd(struct sshbuf *signature, int fd,
if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0) if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
return r; return r;
if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) { debug("%s: signature made with hash \"%s\"", __func__, hashalg);
error("%s: can't look up hash algorithm %s", if ((r = hash_file(fd, hashalg, &b)) != 0) {
__func__, HASHALG_DEFAULT);
return SSH_ERR_INTERNAL_ERROR;
}
if ((r = hash_file(fd, alg, hash, sizeof(hash))) != 0) {
error("%s: hash_file failed: %s", __func__, ssh_err(r)); error("%s: hash_file failed: %s", __func__, ssh_err(r));
return r;
}
if ((b = sshbuf_from(hash, ssh_digest_bytes(alg))) == NULL) {
error("%s: sshbuf_from failed", __func__);
r = SSH_ERR_ALLOC_FAIL;
goto out; goto out;
} }
if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace, if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
@ -587,7 +600,6 @@ sshsig_verify_fd(struct sshbuf *signature, int fd,
out: out:
sshbuf_free(b); sshbuf_free(b);
free(hashalg); free(hashalg);
explicit_bzero(hash, sizeof(hash));
return r; return r;
} }
@ -769,14 +781,14 @@ sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
linenum++; linenum++;
r = check_allowed_keys_line(path, linenum, line, sign_key, r = check_allowed_keys_line(path, linenum, line, sign_key,
principal, sig_namespace); principal, sig_namespace);
free(line);
line = NULL;
if (r == SSH_ERR_KEY_NOT_FOUND) if (r == SSH_ERR_KEY_NOT_FOUND)
continue; continue;
else if (r == 0) { else if (r == 0) {
/* success */ /* success */
fclose(f); fclose(f);
free(line);
return 0; return 0;
/* XXX continue and check revocation? */
} else } else
break; break;
} }

View File

@ -23,15 +23,28 @@ struct sshkey;
typedef int sshsig_signer(struct sshkey *, u_char **, size_t *, typedef int sshsig_signer(struct sshkey *, u_char **, size_t *,
const u_char *, size_t, const char *, u_int, void *); const u_char *, size_t, const char *, u_int, void *);
/* Buffer-oriented API */
/* /*
* Creates a detached SSH signature for a given message. * Creates a detached SSH signature for a given buffer.
* Returns 0 on success or a negative SSH_ERR_* error code on failure. * Returns 0 on success or a negative SSH_ERR_* error code on failure.
* out is populated with the detached signature, or NULL on failure. * out is populated with the detached signature, or NULL on failure.
*/ */
int sshsig_sign_message(struct sshkey *key, const char *hashalg, int sshsig_signb(struct sshkey *key, const char *hashalg,
const struct sshbuf *message, const char *sig_namespace, const struct sshbuf *message, const char *sig_namespace,
struct sshbuf **out, sshsig_signer *signer, void *signer_ctx); 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. * Creates a detached SSH signature for a given file.
* Returns 0 on success or a negative SSH_ERR_* error code on failure. * Returns 0 on success or a negative SSH_ERR_* error code on failure.
@ -41,15 +54,6 @@ int sshsig_sign_fd(struct sshkey *key, const char *hashalg,
int fd, const char *sig_namespace, struct sshbuf **out, int fd, const char *sig_namespace, struct sshbuf **out,
sshsig_signer *signer, void *signer_ctx); 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_verify_message(struct sshbuf *signature,
const struct sshbuf *message, const char *sig_namespace,
struct sshkey **sign_keyp);
/* /*
* Verifies that a detached signature over a file is valid and optionally * Verifies that a detached signature over a file is valid and optionally
* returns key used to sign via argument. * returns key used to sign via argument.
@ -58,6 +62,8 @@ int sshsig_verify_message(struct sshbuf *signature,
int sshsig_verify_fd(struct sshbuf *signature, int fd, int sshsig_verify_fd(struct sshbuf *signature, int fd,
const char *sig_namespace, struct sshkey **sign_keyp); const char *sig_namespace, struct sshkey **sign_keyp);
/* Utility functions */
/* /*
* Return a base64 encoded "ASCII armoured" version of a raw signature. * Return a base64 encoded "ASCII armoured" version of a raw signature.
*/ */