upstream: Improve strictness and control over RSA-SHA2 signature

In ssh, when an agent fails to return a RSA-SHA2 signature when
requested and falls back to RSA-SHA1 instead, retry the signature to
ensure that the public key algorithm sent in the SSH_MSG_USERAUTH
matches the one in the signature itself.

In sshd, strictly enforce that the public key algorithm sent in the
SSH_MSG_USERAUTH message matches what appears in the signature.

Make the sshd_config PubkeyAcceptedKeyTypes and
HostbasedAcceptedKeyTypes options control accepted signature algorithms
(previously they selected supported key types). This allows these
options to ban RSA-SHA1 in favour of RSA-SHA2.

Add new signature algorithms "rsa-sha2-256-cert-v01@openssh.com" and
"rsa-sha2-512-cert-v01@openssh.com" to force use of RSA-SHA2 signatures
with certificate keys.

feedback and ok markus@

OpenBSD-Commit-ID: c6e9f6d45eed8962ad502d315d7eaef32c419dde
This commit is contained in:
djm@openbsd.org 2018-07-03 11:39:54 +00:00 committed by Damien Miller
parent 95344c2574
commit 4ba0d54794
18 changed files with 470 additions and 257 deletions

View File

@ -25,6 +25,10 @@ raw user keys. The ssh client will support automatic verification of
acceptance of certified host keys, by adding a similar ability to acceptance of certified host keys, by adding a similar ability to
specify CA keys in ~/.ssh/known_hosts. specify CA keys in ~/.ssh/known_hosts.
All certificate types include certification information along with the
public key that is used to sign challenges. In OpenSSH, ssh-keygen
performs the CA signing operation.
Certified keys are represented using new key types: Certified keys are represented using new key types:
ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v01@openssh.com
@ -33,9 +37,17 @@ Certified keys are represented using new key types:
ecdsa-sha2-nistp384-cert-v01@openssh.com ecdsa-sha2-nistp384-cert-v01@openssh.com
ecdsa-sha2-nistp521-cert-v01@openssh.com ecdsa-sha2-nistp521-cert-v01@openssh.com
These include certification information along with the public key Two additional types exist for RSA certificates to force use of
that is used to sign challenges. ssh-keygen performs the CA signing SHA-2 signatures (SHA-256 and SHA-512 respectively):
operation.
rsa-sha2-256-cert-v01@openssh.com
rsa-sha2-512-cert-v01@openssh.com
These RSA/SHA-2 types should not appear in keys at rest or transmitted
on their wire, but do appear in a SSH_MSG_KEXINIT's host-key algorithms
field or in the "public key algorithm name" field of a "publickey"
SSH_USERAUTH_REQUEST to indicate that the signature will use the
specified algorithm.
Protocol extensions Protocol extensions
------------------- -------------------
@ -291,4 +303,4 @@ permit-user-rc empty Flag indicating that execution of
of this script will not be permitted if of this script will not be permitted if
this option is not present. this option is not present.
$OpenBSD: PROTOCOL.certkeys,v 1.14 2018/04/10 00:10:49 djm Exp $ $OpenBSD: PROTOCOL.certkeys,v 1.15 2018/07/03 11:39:54 djm Exp $

View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth2-hostbased.c,v 1.33 2018/01/23 05:27:21 djm Exp $ */ /* $OpenBSD: auth2-hostbased.c,v 1.34 2018/07/03 11:39:54 djm Exp $ */
/* /*
* Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2000 Markus Friedl. All rights reserved.
* *
@ -111,8 +111,7 @@ userauth_hostbased(struct ssh *ssh)
"signature format"); "signature format");
goto done; goto done;
} }
if (match_pattern_list(sshkey_ssh_name(key), if (match_pattern_list(pkalg, options.hostbased_key_types, 0) != 1) {
options.hostbased_key_types, 0) != 1) {
logit("%s: key type %s not in HostbasedAcceptedKeyTypes", logit("%s: key type %s not in HostbasedAcceptedKeyTypes",
__func__, sshkey_type(key)); __func__, sshkey_type(key));
goto done; goto done;

View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth2-pubkey.c,v 1.79 2018/06/06 18:29:18 markus Exp $ */ /* $OpenBSD: auth2-pubkey.c,v 1.80 2018/07/03 11:39:54 djm Exp $ */
/* /*
* Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2000 Markus Friedl. All rights reserved.
* *
@ -109,7 +109,7 @@ userauth_pubkey(struct ssh *ssh)
pktype = sshkey_type_from_name(pkalg); pktype = sshkey_type_from_name(pkalg);
if (pktype == KEY_UNSPEC) { if (pktype == KEY_UNSPEC) {
/* this is perfectly legal */ /* this is perfectly legal */
logit("%s: unsupported public key algorithm: %s", verbose("%s: unsupported public key algorithm: %s",
__func__, pkalg); __func__, pkalg);
goto done; goto done;
} }
@ -136,8 +136,7 @@ userauth_pubkey(struct ssh *ssh)
logit("refusing previously-used %s key", sshkey_type(key)); logit("refusing previously-used %s key", sshkey_type(key));
goto done; goto done;
} }
if (match_pattern_list(sshkey_ssh_name(key), if (match_pattern_list(pkalg, options.pubkey_key_types, 0) != 1) {
options.pubkey_key_types, 0) != 1) {
logit("%s: key type %s not in PubkeyAcceptedKeyTypes", logit("%s: key type %s not in PubkeyAcceptedKeyTypes",
__func__, sshkey_ssh_name(key)); __func__, sshkey_ssh_name(key));
goto done; goto done;
@ -188,8 +187,10 @@ userauth_pubkey(struct ssh *ssh)
/* test for correct signature */ /* test for correct signature */
authenticated = 0; authenticated = 0;
if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) && if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) &&
PRIVSEP(sshkey_verify(key, sig, slen, sshbuf_ptr(b), PRIVSEP(sshkey_verify(key, sig, slen,
sshbuf_len(b), NULL, ssh->compat)) == 0) { sshbuf_ptr(b), sshbuf_len(b),
(ssh->compat & SSH_BUG_SIGTYPE) == 0 ? pkalg : NULL,
ssh->compat)) == 0) {
authenticated = 1; authenticated = 1;
} }
sshbuf_free(b); sshbuf_free(b);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: authfd.c,v 1.109 2018/04/10 00:10:49 djm Exp $ */ /* $OpenBSD: authfd.c,v 1.110 2018/07/03 11:39:54 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
@ -343,8 +343,8 @@ ssh_agent_sign(int sock, const struct sshkey *key,
const u_char *data, size_t datalen, const char *alg, u_int compat) const u_char *data, size_t datalen, const char *alg, u_int compat)
{ {
struct sshbuf *msg; struct sshbuf *msg;
u_char *blob = NULL, type; u_char *sig = NULL, type = 0;
size_t blen = 0, len = 0; size_t len = 0;
u_int flags = 0; u_int flags = 0;
int r = SSH_ERR_INTERNAL_ERROR; int r = SSH_ERR_INTERNAL_ERROR;
@ -355,11 +355,9 @@ ssh_agent_sign(int sock, const struct sshkey *key,
return SSH_ERR_INVALID_ARGUMENT; return SSH_ERR_INVALID_ARGUMENT;
if ((msg = sshbuf_new()) == NULL) if ((msg = sshbuf_new()) == NULL)
return SSH_ERR_ALLOC_FAIL; return SSH_ERR_ALLOC_FAIL;
if ((r = sshkey_to_blob(key, &blob, &blen)) != 0)
goto out;
flags |= agent_encode_alg(key, alg); flags |= agent_encode_alg(key, alg);
if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 ||
(r = sshbuf_put_string(msg, blob, blen)) != 0 || (r = sshkey_puts(key, msg)) != 0 ||
(r = sshbuf_put_string(msg, data, datalen)) != 0 || (r = sshbuf_put_string(msg, data, datalen)) != 0 ||
(r = sshbuf_put_u32(msg, flags)) != 0) (r = sshbuf_put_u32(msg, flags)) != 0)
goto out; goto out;
@ -374,15 +372,19 @@ ssh_agent_sign(int sock, const struct sshkey *key,
r = SSH_ERR_INVALID_FORMAT; r = SSH_ERR_INVALID_FORMAT;
goto out; goto out;
} }
if ((r = sshbuf_get_string(msg, sigp, &len)) != 0) if ((r = sshbuf_get_string(msg, &sig, &len)) != 0)
goto out; goto out;
/* Check what we actually got back from the agent. */
if ((r = sshkey_check_sigtype(sig, len, alg)) != 0)
goto out;
/* success */
*sigp = sig;
*lenp = len; *lenp = len;
sig = NULL;
len = 0;
r = 0; r = 0;
out: out:
if (blob != NULL) { freezero(sig, len);
explicit_bzero(blob, blen);
free(blob);
}
sshbuf_free(msg); sshbuf_free(msg);
return r; return r;
} }

View File

@ -1,4 +1,4 @@
/* $OpenBSD: compat.c,v 1.107 2018/04/16 22:50:44 djm Exp $ */ /* $OpenBSD: compat.c,v 1.108 2018/07/03 11:39:54 djm Exp $ */
/* /*
* Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved.
* *
@ -52,16 +52,27 @@ compat_datafellows(const char *version)
} check[] = { } check[] = {
{ "OpenSSH_2.*," { "OpenSSH_2.*,"
"OpenSSH_3.0*," "OpenSSH_3.0*,"
"OpenSSH_3.1*", SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, "OpenSSH_3.1*", SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR|
{ "OpenSSH_3.*", SSH_OLD_FORWARD_ADDR }, SSH_BUG_SIGTYPE},
{ "Sun_SSH_1.0*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF}, { "OpenSSH_3.*", SSH_OLD_FORWARD_ADDR|SSH_BUG_SIGTYPE },
{ "Sun_SSH_1.0*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF|
SSH_BUG_SIGTYPE},
{ "OpenSSH_2*," { "OpenSSH_2*,"
"OpenSSH_3*," "OpenSSH_3*,"
"OpenSSH_4*", 0 }, "OpenSSH_4*", SSH_BUG_SIGTYPE },
{ "OpenSSH_5*", SSH_NEW_OPENSSH|SSH_BUG_DYNAMIC_RPORT}, { "OpenSSH_5*", SSH_NEW_OPENSSH|SSH_BUG_DYNAMIC_RPORT|
{ "OpenSSH_6.6.1*", SSH_NEW_OPENSSH}, SSH_BUG_SIGTYPE},
{ "OpenSSH_6.6.1*", SSH_NEW_OPENSSH|SSH_BUG_SIGTYPE},
{ "OpenSSH_6.5*," { "OpenSSH_6.5*,"
"OpenSSH_6.6*", SSH_NEW_OPENSSH|SSH_BUG_CURVE25519PAD}, "OpenSSH_6.6*", SSH_NEW_OPENSSH|SSH_BUG_CURVE25519PAD|
SSH_BUG_SIGTYPE},
{ "OpenSSH_7.0*,"
"OpenSSH_7.1*,"
"OpenSSH_7.2*,"
"OpenSSH_7.3*,"
"OpenSSH_7.4*,"
"OpenSSH_7.5*,"
"OpenSSH_7.6*", SSH_NEW_OPENSSH|SSH_BUG_SIGTYPE},
{ "OpenSSH*", SSH_NEW_OPENSSH }, { "OpenSSH*", SSH_NEW_OPENSSH },
{ "*MindTerm*", 0 }, { "*MindTerm*", 0 },
{ "3.0.*", SSH_BUG_DEBUG }, { "3.0.*", SSH_BUG_DEBUG },

View File

@ -1,4 +1,4 @@
/* $OpenBSD: compat.h,v 1.51 2018/02/16 04:43:11 dtucker Exp $ */ /* $OpenBSD: compat.h,v 1.52 2018/07/03 11:39:54 djm Exp $ */
/* /*
* Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved.
@ -33,7 +33,7 @@
#define SSH_PROTO_2 0x04 #define SSH_PROTO_2 0x04
#define SSH_BUG_UTF8TTYMODE 0x00000001 #define SSH_BUG_UTF8TTYMODE 0x00000001
/* #define unused 0x00000002 */ #define SSH_BUG_SIGTYPE 0x00000002
/* #define unused 0x00000004 */ /* #define unused 0x00000004 */
/* #define unused 0x00000008 */ /* #define unused 0x00000008 */
#define SSH_OLD_SESSIONID 0x00000010 #define SSH_OLD_SESSIONID 0x00000010

17
kex.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: kex.c,v 1.136 2018/02/07 02:06:50 jsing Exp $ */ /* $OpenBSD: kex.c,v 1.137 2018/07/03 11:39:54 djm Exp $ */
/* /*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
* *
@ -342,6 +342,7 @@ kex_send_ext_info(struct ssh *ssh)
if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL) if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
return SSH_ERR_ALLOC_FAIL; return SSH_ERR_ALLOC_FAIL;
/* XXX filter algs list by allowed pubkey/hostbased types */
if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 || if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
(r = sshpkt_put_u32(ssh, 1)) != 0 || (r = sshpkt_put_u32(ssh, 1)) != 0 ||
(r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 || (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
@ -378,7 +379,7 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
{ {
struct kex *kex = ssh->kex; struct kex *kex = ssh->kex;
u_int32_t i, ninfo; u_int32_t i, ninfo;
char *name, *found; char *name;
u_char *val; u_char *val;
size_t vlen; size_t vlen;
int r; int r;
@ -401,16 +402,8 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
return SSH_ERR_INVALID_FORMAT; return SSH_ERR_INVALID_FORMAT;
} }
debug("%s: %s=<%s>", __func__, name, val); debug("%s: %s=<%s>", __func__, name, val);
found = match_list("rsa-sha2-256", val, NULL); kex->server_sig_algs = val;
if (found) { val = NULL;
kex->rsa_sha2 = 256;
free(found);
}
found = match_list("rsa-sha2-512", val, NULL);
if (found) {
kex->rsa_sha2 = 512;
free(found);
}
} else } else
debug("%s: %s (unrecognised)", __func__, name); debug("%s: %s (unrecognised)", __func__, name);
free(name); free(name);

4
kex.h
View File

@ -1,4 +1,4 @@
/* $OpenBSD: kex.h,v 1.83 2017/05/30 14:23:52 markus Exp $ */ /* $OpenBSD: kex.h,v 1.84 2018/07/03 11:39:54 djm Exp $ */
/* /*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@ -139,7 +139,7 @@ struct kex {
int hostkey_type; int hostkey_type;
int hostkey_nid; int hostkey_nid;
u_int kex_type; u_int kex_type;
int rsa_sha2; char *server_sig_algs;
int ext_info_c; int ext_info_c;
struct sshbuf *my; struct sshbuf *my;
struct sshbuf *peer; struct sshbuf *peer;

View File

@ -1,4 +1,4 @@
/* $OpenBSD: myproposal.h,v 1.55 2017/05/07 23:13:42 djm Exp $ */ /* $OpenBSD: myproposal.h,v 1.56 2018/07/03 11:39:54 djm Exp $ */
/* /*
* Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2000 Markus Friedl. All rights reserved.
@ -107,6 +107,8 @@
#define KEX_DEFAULT_PK_ALG \ #define KEX_DEFAULT_PK_ALG \
HOSTKEY_ECDSA_CERT_METHODS \ HOSTKEY_ECDSA_CERT_METHODS \
"ssh-ed25519-cert-v01@openssh.com," \ "ssh-ed25519-cert-v01@openssh.com," \
"rsa-sha2-512-cert-v01@openssh.com," \
"rsa-sha2-256-cert-v01@openssh.com," \
"ssh-rsa-cert-v01@openssh.com," \ "ssh-rsa-cert-v01@openssh.com," \
HOSTKEY_ECDSA_METHODS \ HOSTKEY_ECDSA_METHODS \
"ssh-ed25519," \ "ssh-ed25519," \

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-rsa.c,v 1.66 2018/02/14 16:27:24 jsing Exp $ */ /* $OpenBSD: ssh-rsa.c,v 1.67 2018/07/03 11:39:54 djm Exp $ */
/* /*
* Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org> * Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org>
* *
@ -51,11 +51,14 @@ rsa_hash_alg_ident(int hash_alg)
return NULL; return NULL;
} }
/*
* Returns the hash algorithm ID for a given algorithm identifier as used
* inside the signature blob,
*/
static int static int
rsa_hash_alg_from_ident(const char *ident) rsa_hash_id_from_ident(const char *ident)
{ {
if (strcmp(ident, "ssh-rsa") == 0 || if (strcmp(ident, "ssh-rsa") == 0)
strcmp(ident, "ssh-rsa-cert-v01@openssh.com") == 0)
return SSH_DIGEST_SHA1; return SSH_DIGEST_SHA1;
if (strcmp(ident, "rsa-sha2-256") == 0) if (strcmp(ident, "rsa-sha2-256") == 0)
return SSH_DIGEST_SHA256; return SSH_DIGEST_SHA256;
@ -64,6 +67,27 @@ rsa_hash_alg_from_ident(const char *ident)
return -1; return -1;
} }
/*
* Return the hash algorithm ID for the specified key name. This includes
* all the cases of rsa_hash_id_from_ident() but also the certificate key
* types.
*/
static int
rsa_hash_id_from_keyname(const char *alg)
{
int r;
if ((r = rsa_hash_id_from_ident(alg)) != -1)
return r;
if (strcmp(alg, "ssh-rsa-cert-v01@openssh.com") == 0)
return SSH_DIGEST_SHA1;
if (strcmp(alg, "rsa-sha2-256-cert-v01@openssh.com") == 0)
return SSH_DIGEST_SHA256;
if (strcmp(alg, "rsa-sha2-512-cert-v01@openssh.com") == 0)
return SSH_DIGEST_SHA512;
return -1;
}
static int static int
rsa_hash_alg_nid(int type) rsa_hash_alg_nid(int type)
{ {
@ -135,7 +159,7 @@ ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
if (alg_ident == NULL || strlen(alg_ident) == 0) if (alg_ident == NULL || strlen(alg_ident) == 0)
hash_alg = SSH_DIGEST_SHA1; hash_alg = SSH_DIGEST_SHA1;
else else
hash_alg = rsa_hash_alg_from_ident(alg_ident); hash_alg = rsa_hash_id_from_keyname(alg_ident);
if (key == NULL || key->rsa == NULL || hash_alg == -1 || if (key == NULL || key->rsa == NULL || hash_alg == -1 ||
sshkey_type_plain(key->type) != KEY_RSA) sshkey_type_plain(key->type) != KEY_RSA)
return SSH_ERR_INVALID_ARGUMENT; return SSH_ERR_INVALID_ARGUMENT;
@ -202,7 +226,7 @@ ssh_rsa_verify(const struct sshkey *key,
const char *alg) const char *alg)
{ {
char *sigtype = NULL; char *sigtype = NULL;
int hash_alg, ret = SSH_ERR_INTERNAL_ERROR; int hash_alg, want_alg, ret = SSH_ERR_INTERNAL_ERROR;
size_t len = 0, diff, modlen, dlen; size_t len = 0, diff, modlen, dlen;
struct sshbuf *b = NULL; struct sshbuf *b = NULL;
u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL; u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL;
@ -220,18 +244,24 @@ ssh_rsa_verify(const struct sshkey *key,
ret = SSH_ERR_INVALID_FORMAT; ret = SSH_ERR_INVALID_FORMAT;
goto out; goto out;
} }
/* XXX djm: need cert types that reliably yield SHA-2 signatures */ if ((hash_alg = rsa_hash_id_from_ident(sigtype)) == -1) {
if (alg != NULL && strcmp(alg, sigtype) != 0 &&
strcmp(alg, "ssh-rsa-cert-v01@openssh.com") != 0) {
error("%s: RSA signature type mismatch: "
"expected %s received %s", __func__, alg, sigtype);
ret = SSH_ERR_SIGNATURE_INVALID;
goto out;
}
if ((hash_alg = rsa_hash_alg_from_ident(sigtype)) == -1) {
ret = SSH_ERR_KEY_TYPE_MISMATCH; ret = SSH_ERR_KEY_TYPE_MISMATCH;
goto out; goto out;
} }
/*
* Allow ssh-rsa-cert-v01 certs to generate SHA2 signatures for
* legacy reasons, but otherwise the signature type should match.
*/
if (alg != NULL && strcmp(alg, "ssh-rsa-cert-v01@openssh.com") != 0) {
if ((want_alg = rsa_hash_id_from_keyname(alg)) == -1) {
ret = SSH_ERR_INVALID_ARGUMENT;
goto out;
}
if (hash_alg != want_alg) {
ret = SSH_ERR_SIGNATURE_INVALID;
goto out;
}
}
if (sshbuf_get_string(b, &sigblob, &len) != 0) { if (sshbuf_get_string(b, &sigblob, &len) != 0) {
ret = SSH_ERR_INVALID_FORMAT; ret = SSH_ERR_INVALID_FORMAT;
goto out; goto out;

View File

@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\" .\"
.\" $OpenBSD: ssh_config.5,v 1.277 2018/06/09 06:36:31 jmc Exp $ .\" $OpenBSD: ssh_config.5,v 1.278 2018/07/03 11:39:54 djm Exp $
.Dd $Mdocdate: June 9 2018 $ .Dd $Mdocdate: July 3 2018 $
.Dt SSH_CONFIG 5 .Dt SSH_CONFIG 5
.Os .Os
.Sh NAME .Sh NAME
@ -772,9 +772,10 @@ ecdsa-sha2-nistp256-cert-v01@openssh.com,
ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com,
ecdsa-sha2-nistp521-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com,
ssh-ed25519-cert-v01@openssh.com, ssh-ed25519-cert-v01@openssh.com,
rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,
ssh-rsa-cert-v01@openssh.com, ssh-rsa-cert-v01@openssh.com,
ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
ssh-ed25519,ssh-rsa ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
.Ed .Ed
.Pp .Pp
The The
@ -799,9 +800,10 @@ ecdsa-sha2-nistp256-cert-v01@openssh.com,
ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com,
ecdsa-sha2-nistp521-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com,
ssh-ed25519-cert-v01@openssh.com, ssh-ed25519-cert-v01@openssh.com,
rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,
ssh-rsa-cert-v01@openssh.com, ssh-rsa-cert-v01@openssh.com,
ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
ssh-ed25519,ssh-rsa ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
.Ed .Ed
.Pp .Pp
If hostkeys are known for the destination host then this default is modified If hostkeys are known for the destination host then this default is modified
@ -1255,9 +1257,10 @@ ecdsa-sha2-nistp256-cert-v01@openssh.com,
ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com,
ecdsa-sha2-nistp521-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com,
ssh-ed25519-cert-v01@openssh.com, ssh-ed25519-cert-v01@openssh.com,
rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,
ssh-rsa-cert-v01@openssh.com, ssh-rsa-cert-v01@openssh.com,
ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
ssh-ed25519,ssh-rsa ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
.Ed .Ed
.Pp .Pp
The list of available key types may also be obtained using The list of available key types may also be obtained using

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshconnect2.c,v 1.271 2018/06/26 02:02:36 djm Exp $ */ /* $OpenBSD: sshconnect2.c,v 1.272 2018/07/03 11:39:54 djm Exp $ */
/* /*
* Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Damien Miller. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved.
@ -315,7 +315,7 @@ int input_gssapi_errtok(int, u_int32_t, struct ssh *);
void userauth(Authctxt *, char *); void userauth(Authctxt *, char *);
static int sign_and_send_pubkey(Authctxt *, Identity *); static int sign_and_send_pubkey(struct ssh *ssh, Authctxt *, Identity *);
static void pubkey_prepare(Authctxt *); static void pubkey_prepare(Authctxt *);
static void pubkey_cleanup(Authctxt *); static void pubkey_cleanup(Authctxt *);
static void pubkey_reset(Authctxt *); static void pubkey_reset(Authctxt *);
@ -619,7 +619,7 @@ input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh)
*/ */
TAILQ_FOREACH_REVERSE(id, &authctxt->keys, idlist, next) { TAILQ_FOREACH_REVERSE(id, &authctxt->keys, idlist, next) {
if (key_equal(key, id->key)) { if (key_equal(key, id->key)) {
sent = sign_and_send_pubkey(authctxt, id); sent = sign_and_send_pubkey(ssh, authctxt, id);
break; break;
} }
} }
@ -986,73 +986,80 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, struct ssh *ssh)
return 0; return 0;
} }
static const char *
key_sign_encode(const struct sshkey *key)
{
struct ssh *ssh = active_state;
if (key->type == KEY_RSA) {
switch (ssh->kex->rsa_sha2) {
case 256:
return "rsa-sha2-256";
case 512:
return "rsa-sha2-512";
}
}
return key_ssh_name(key);
}
/* /*
* Some agents will return ssh-rsa signatures when asked to make a * Select an algorithm for publickey signatures.
* rsa-sha2-* signature. Check what they actually gave back and warn the * Returns algorithm (caller must free) or NULL if no mutual algorithm found.
* user if the agent has returned an unexpected type. *
* Call with ssh==NULL to ignore server-sig-algs extension list and
* only attempt with the key's base signature type.
*/ */
static int static char *
check_sigtype(const struct sshkey *key, const u_char *sig, size_t len) key_sig_algorithm(struct ssh *ssh, const struct sshkey *key)
{ {
int r; char *allowed, *oallowed, *cp, *alg = NULL;
char *sigtype = NULL;
const char *alg = key_sign_encode(key);
if (sshkey_is_cert(key)) /*
return 0; * The signature algorithm will only differ from the key algorithm
if ((r = sshkey_sigtype(sig, len, &sigtype)) != 0) * for RSA keys/certs and when the server advertises support for
return r; * newer (SHA2) algorithms.
if (strcmp(sigtype, alg) != 0) { */
logit("warning: agent returned different signature type %s " if (ssh == NULL || ssh->kex->server_sig_algs == NULL ||
"(expected %s)", sigtype, alg); (key->type != KEY_RSA && key->type != KEY_RSA_CERT)) {
/* Filter base key signature alg against our configuration */
return match_list(key_ssh_name(key),
options.pubkey_key_types, NULL);
} }
free(sigtype);
/* Incorrect signature types aren't an error ... yet */ /*
return 0; * For RSA keys/certs, since these might have a different sig type:
* find the first entry in PubkeyAcceptedKeyTypes of the right type
* that also appears in the supported signature algorithms list from
* the server.
*/
oallowed = allowed = xstrdup(options.pubkey_key_types);
while ((cp = strsep(&allowed, ",")) != NULL) {
if (sshkey_type_from_name(cp) != key->type)
continue;
alg = match_list(cp, ssh->kex->server_sig_algs, NULL);
if (alg != NULL)
break;
}
free(oallowed);
return alg;
} }
static int static int
identity_sign(struct identity *id, u_char **sigp, size_t *lenp, identity_sign(struct identity *id, u_char **sigp, size_t *lenp,
const u_char *data, size_t datalen, u_int compat) const u_char *data, size_t datalen, u_int compat, const char *alg)
{ {
struct sshkey *prv; struct sshkey *prv;
int r; int r;
/* the agent supports this key */ /* The agent supports this key. */
if (id->key != NULL && id->agent_fd != -1) { if (id->key != NULL && id->agent_fd != -1) {
if ((r = ssh_agent_sign(id->agent_fd, id->key, sigp, lenp, return ssh_agent_sign(id->agent_fd, id->key, sigp, lenp,
data, datalen, key_sign_encode(id->key), compat)) != 0 || data, datalen, alg, compat);
(r = check_sigtype(id->key, *sigp, *lenp)) != 0) }
/*
* We have already loaded the private key or the private key is
* stored in external hardware.
*/
if (id->key != NULL &&
(id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))) {
if ((r = sshkey_sign(id->key, sigp, lenp, data, datalen,
alg, compat)) != 0)
return r;
/*
* PKCS#11 tokens may not support all signature algorithms,
* so check what we get back.
*/
if ((r = sshkey_check_sigtype(*sigp, *lenp, alg)) != 0)
return r; return r;
return 0; return 0;
} }
/* /* Load the private key from the file. */
* we have already loaded the private key or
* the private key is stored in external hardware
*/
if (id->key != NULL &&
(id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT)))
return (sshkey_sign(id->key, sigp, lenp, data, datalen,
key_sign_encode(id->key), compat));
/* load the private key from the file */
if ((prv = load_identity_file(id)) == NULL) if ((prv = load_identity_file(id)) == NULL)
return SSH_ERR_KEY_NOT_FOUND; return SSH_ERR_KEY_NOT_FOUND;
if (id->key != NULL && !sshkey_equal_public(prv, id->key)) { if (id->key != NULL && !sshkey_equal_public(prv, id->key)) {
@ -1060,8 +1067,7 @@ identity_sign(struct identity *id, u_char **sigp, size_t *lenp,
__func__, id->filename); __func__, id->filename);
return SSH_ERR_KEY_NOT_FOUND; return SSH_ERR_KEY_NOT_FOUND;
} }
r = sshkey_sign(prv, sigp, lenp, data, datalen, r = sshkey_sign(prv, sigp, lenp, data, datalen, alg, compat);
key_sign_encode(prv), compat);
sshkey_free(prv); sshkey_free(prv);
return r; return r;
} }
@ -1086,57 +1092,35 @@ id_filename_matches(Identity *id, Identity *private_id)
} }
static int static int
sign_and_send_pubkey(Authctxt *authctxt, Identity *id) sign_and_send_pubkey(struct ssh *ssh, Authctxt *authctxt, Identity *id)
{ {
Buffer b; struct sshbuf *b = NULL;
Identity *private_id; Identity *private_id, *sign_id = NULL;
u_char *blob, *signature; u_char *signature = NULL;
size_t slen; size_t slen = 0, skip = 0;
u_int bloblen, skip = 0; int r, fallback_sigtype, sent = 0;
int matched, ret = -1, have_sig = 1; char *alg = NULL, *fp = NULL;
char *fp; const char *loc = "";
if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash, if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash,
SSH_FP_DEFAULT)) == NULL) SSH_FP_DEFAULT)) == NULL)
return 0; return 0;
debug3("%s: %s %s", __func__, key_type(id->key), fp);
free(fp);
if (key_to_blob(id->key, &blob, &bloblen) == 0) { debug3("%s: %s %s", __func__, sshkey_type(id->key), fp);
/* we cannot handle this key */
debug3("sign_and_send_pubkey: cannot handle key");
return 0;
}
/* data to be signed */
buffer_init(&b);
if (datafellows & SSH_OLD_SESSIONID) {
buffer_append(&b, session_id2, session_id2_len);
skip = session_id2_len;
} else {
buffer_put_string(&b, session_id2, session_id2_len);
skip = buffer_len(&b);
}
buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
buffer_put_cstring(&b, authctxt->server_user);
buffer_put_cstring(&b, authctxt->service);
buffer_put_cstring(&b, authctxt->method->name);
buffer_put_char(&b, have_sig);
buffer_put_cstring(&b, key_sign_encode(id->key));
buffer_put_string(&b, blob, bloblen);
/* /*
* If the key is an certificate, try to find a matching private key * If the key is an certificate, try to find a matching private key
* and use it to complete the signature. * and use it to complete the signature.
* If no such private key exists, fall back to trying the certificate * If no such private key exists, fall back to trying the certificate
* key itself in case it has a private half already loaded. * key itself in case it has a private half already loaded.
* This will try to set sign_id to the private key that will perform
* the signature.
*/ */
if (key_is_cert(id->key)) { if (sshkey_is_cert(id->key)) {
matched = 0;
TAILQ_FOREACH(private_id, &authctxt->keys, next) { TAILQ_FOREACH(private_id, &authctxt->keys, next) {
if (sshkey_equal_public(id->key, private_id->key) && if (sshkey_equal_public(id->key, private_id->key) &&
id->key->type != private_id->key->type) { id->key->type != private_id->key->type) {
id = private_id; sign_id = private_id;
matched = 1;
break; break;
} }
} }
@ -1147,18 +1131,18 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id)
* of keeping just a private key file and public * of keeping just a private key file and public
* certificate on disk. * certificate on disk.
*/ */
if (!matched && !id->isprivate && id->agent_fd == -1 && if (sign_id == NULL &&
!id->isprivate && id->agent_fd == -1 &&
(id->key->flags & SSHKEY_FLAG_EXT) == 0) { (id->key->flags & SSHKEY_FLAG_EXT) == 0) {
TAILQ_FOREACH(private_id, &authctxt->keys, next) { TAILQ_FOREACH(private_id, &authctxt->keys, next) {
if (private_id->key == NULL && if (private_id->key == NULL &&
id_filename_matches(id, private_id)) { id_filename_matches(id, private_id)) {
id = private_id; sign_id = private_id;
matched = 1;
break; break;
} }
} }
} }
if (matched) { if (sign_id != NULL) {
debug2("%s: using private key \"%s\"%s for " debug2("%s: using private key \"%s\"%s for "
"certificate", __func__, id->filename, "certificate", __func__, id->filename,
id->agent_fd != -1 ? " from agent" : ""); id->agent_fd != -1 ? " from agent" : "");
@ -1168,51 +1152,121 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id)
} }
} }
/* generate signature */ /*
ret = identity_sign(id, &signature, &slen, * If the above didn't select another identity to do the signing
buffer_ptr(&b), buffer_len(&b), datafellows); * then default to the one we started with.
if (ret != 0) { */
if (ret != SSH_ERR_KEY_NOT_FOUND) if (sign_id == NULL)
error("%s: signing failed: %s", __func__, ssh_err(ret)); sign_id = id;
free(blob);
buffer_free(&b); /* assemble and sign data */
return 0; for (fallback_sigtype = 0; fallback_sigtype <= 1; fallback_sigtype++) {
free(alg);
slen = 0;
signature = NULL;
if ((alg = key_sig_algorithm(fallback_sigtype ? NULL : ssh,
id->key)) == NULL) {
error("%s: no mutual signature supported", __func__);
goto out;
}
debug3("%s: signing using %s", __func__, alg);
sshbuf_free(b);
if ((b = sshbuf_new()) == NULL)
fatal("%s: sshbuf_new failed", __func__);
if (datafellows & SSH_OLD_SESSIONID) {
if ((r = sshbuf_put(b, session_id2,
session_id2_len)) != 0) {
fatal("%s: sshbuf_put: %s",
__func__, ssh_err(r));
}
} else {
if ((r = sshbuf_put_string(b, session_id2,
session_id2_len)) != 0) {
fatal("%s: sshbuf_put_string: %s",
__func__, ssh_err(r));
}
}
skip = buffer_len(b);
if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
(r = sshbuf_put_cstring(b, authctxt->server_user)) != 0 ||
(r = sshbuf_put_cstring(b, authctxt->service)) != 0 ||
(r = sshbuf_put_cstring(b, authctxt->method->name)) != 0 ||
(r = sshbuf_put_u8(b, 1)) != 0 ||
(r = sshbuf_put_cstring(b, alg)) != 0 ||
(r = sshkey_puts(id->key, b)) != 0) {
fatal("%s: assemble signed data: %s",
__func__, ssh_err(r));
}
/* generate signature */
r = identity_sign(sign_id, &signature, &slen,
sshbuf_ptr(b), sshbuf_len(b), datafellows, alg);
if (r == 0)
break;
else if (r == SSH_ERR_KEY_NOT_FOUND)
goto out; /* soft failure */
else if (r == SSH_ERR_SIGN_ALG_UNSUPPORTED &&
!fallback_sigtype) {
if (sign_id->agent_fd != -1)
loc = "agent ";
else if ((sign_id->key->flags & SSHKEY_FLAG_EXT) != 0)
loc = "token ";
logit("%skey %s %s returned incorrect signature type",
loc, sshkey_type(id->key), fp);
continue;
}
error("%s: signing failed: %s", __func__, ssh_err(r));
goto out;
} }
#ifdef DEBUG_PK if (slen == 0 || signature == NULL) /* shouldn't happen */
buffer_dump(&b); fatal("%s: no signature", __func__);
#endif
free(blob);
/* append signature */ /* append signature */
buffer_put_string(&b, signature, slen); if ((r = sshbuf_put_string(b, signature, slen)) != 0)
free(signature); fatal("%s: append signature: %s", __func__, ssh_err(r));
#ifdef DEBUG_PK
sshbuf_dump(b, stderr);
#endif
/* skip session id and packet type */ /* skip session id and packet type */
if (buffer_len(&b) < skip + 1) if ((r = sshbuf_consume(b, skip + 1)) != 0)
fatal("userauth_pubkey: internal error"); fatal("%s: consume: %s", __func__, ssh_err(r));
buffer_consume(&b, skip + 1);
/* put remaining data from buffer into packet */ /* put remaining data from buffer into packet */
packet_start(SSH2_MSG_USERAUTH_REQUEST); if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
packet_put_raw(buffer_ptr(&b), buffer_len(&b)); (r = sshpkt_putb(ssh, b)) != 0 ||
buffer_free(&b); (r = sshpkt_send(ssh)) != 0)
packet_send(); fatal("%s: enqueue request: %s", __func__, ssh_err(r));
return 1; /* success */
sent = 1;
out:
free(fp);
free(alg);
sshbuf_free(b);
freezero(signature, slen);
return sent;
} }
static int static int
send_pubkey_test(Authctxt *authctxt, Identity *id) send_pubkey_test(struct ssh *ssh, Authctxt *authctxt, Identity *id)
{ {
u_char *blob; u_char *blob = NULL;
u_int bloblen, have_sig = 0; u_int bloblen, have_sig = 0;
char *alg = NULL;
int sent = 0;
debug3("send_pubkey_test"); if ((alg = key_sig_algorithm(ssh, id->key)) == NULL) {
debug("%s: no mutual signature algorithm", __func__);
goto out;
}
if (key_to_blob(id->key, &blob, &bloblen) == 0) { if (key_to_blob(id->key, &blob, &bloblen) == 0) {
/* we cannot handle this key */ /* we cannot handle this key */
debug3("send_pubkey_test: cannot handle key"); debug3("%s: cannot handle key", __func__);
return 0; goto out;
} }
/* register callback for USERAUTH_PK_OK message */ /* register callback for USERAUTH_PK_OK message */
dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok); dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok);
@ -1222,11 +1276,15 @@ send_pubkey_test(Authctxt *authctxt, Identity *id)
packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->service);
packet_put_cstring(authctxt->method->name); packet_put_cstring(authctxt->method->name);
packet_put_char(have_sig); packet_put_char(have_sig);
packet_put_cstring(key_sign_encode(id->key)); packet_put_cstring(alg);
packet_put_string(blob, bloblen); packet_put_string(blob, bloblen);
free(blob);
packet_send(); packet_send();
return 1; /* success */
sent = 1;
out:
free(alg);
free(blob);
return sent;
} }
static struct sshkey * static struct sshkey *
@ -1295,6 +1353,36 @@ load_identity_file(Identity *id)
return private; return private;
} }
static int
key_type_allowed_by_config(struct sshkey *key)
{
if (match_pattern_list(sshkey_ssh_name(key),
options.pubkey_key_types, 0) == 1)
return 1;
/* RSA keys/certs might be allowed by alternate signature types */
switch (key->type) {
case KEY_RSA:
if (match_pattern_list("rsa-sha2-512",
options.pubkey_key_types, 0) == 1)
return 1;
if (match_pattern_list("rsa-sha2-256",
options.pubkey_key_types, 0) == 1)
return 1;
break;
case KEY_RSA_CERT:
if (match_pattern_list("rsa-sha2-512-cert-v01@openssh.com",
options.pubkey_key_types, 0) == 1)
return 1;
if (match_pattern_list("rsa-sha2-256-cert-v01@openssh.com",
options.pubkey_key_types, 0) == 1)
return 1;
break;
}
return 0;
}
/* /*
* try keys in the following order: * try keys in the following order:
* 1. certificates listed in the config file * 1. certificates listed in the config file
@ -1419,9 +1507,7 @@ pubkey_prepare(Authctxt *authctxt)
} }
/* finally, filter by PubkeyAcceptedKeyTypes */ /* finally, filter by PubkeyAcceptedKeyTypes */
TAILQ_FOREACH_SAFE(id, preferred, next, id2) { TAILQ_FOREACH_SAFE(id, preferred, next, id2) {
if (id->key != NULL && if (id->key != NULL && !key_type_allowed_by_config(key)) {
match_pattern_list(sshkey_ssh_name(id->key),
options.pubkey_key_types, 0) != 1) {
debug("Skipping %s key %s - " debug("Skipping %s key %s - "
"not in PubkeyAcceptedKeyTypes", "not in PubkeyAcceptedKeyTypes",
sshkey_ssh_name(id->key), id->filename); sshkey_ssh_name(id->key), id->filename);
@ -1479,6 +1565,7 @@ try_identity(Identity *id)
int int
userauth_pubkey(Authctxt *authctxt) userauth_pubkey(Authctxt *authctxt)
{ {
struct ssh *ssh = active_state; /* XXX */
Identity *id; Identity *id;
int sent = 0; int sent = 0;
char *fp; char *fp;
@ -1506,7 +1593,7 @@ userauth_pubkey(Authctxt *authctxt)
debug("Offering public key: %s %s %s", debug("Offering public key: %s %s %s",
sshkey_type(id->key), fp, id->filename); sshkey_type(id->key), fp, id->filename);
free(fp); free(fp);
sent = send_pubkey_test(authctxt, id); sent = send_pubkey_test(ssh, authctxt, id);
} }
} else { } else {
debug("Trying private key: %s", id->filename); debug("Trying private key: %s", id->filename);
@ -1514,7 +1601,7 @@ userauth_pubkey(Authctxt *authctxt)
if (id->key != NULL) { if (id->key != NULL) {
if (try_identity(id)) { if (try_identity(id)) {
id->isprivate = 1; id->isprivate = 1;
sent = sign_and_send_pubkey( sent = sign_and_send_pubkey(ssh,
authctxt, id); authctxt, id);
} }
key_free(id->key); key_free(id->key);
@ -1735,7 +1822,7 @@ ssh_keysign(struct sshkey *key, u_char **sigp, size_t *lenp,
int int
userauth_hostbased(Authctxt *authctxt) userauth_hostbased(Authctxt *authctxt)
{ {
struct ssh *ssh = active_state; struct ssh *ssh = active_state; /* XXX */
struct sshkey *private = NULL; struct sshkey *private = NULL;
struct sshbuf *b = NULL; struct sshbuf *b = NULL;
u_char *sig = NULL, *keyblob = NULL; u_char *sig = NULL, *keyblob = NULL;

63
sshd.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshd.c,v 1.508 2018/04/13 03:57:26 dtucker Exp $ */ /* $OpenBSD: sshd.c,v 1.509 2018/07/03 11:39:54 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
@ -681,45 +681,47 @@ privsep_postauth(Authctxt *authctxt)
packet_set_authenticated(); packet_set_authenticated();
} }
static void
append_hostkey_type(struct sshbuf *b, const char *s)
{
int r;
if (match_pattern_list(s, options.hostkeyalgorithms, 0) != 1) {
debug3("%s: %s key not permitted by HostkeyAlgorithms",
__func__, s);
return;
}
if ((r = sshbuf_putf(b, "%s%s", sshbuf_len(b) > 0 ? "," : "", s)) != 0)
fatal("%s: sshbuf_putf: %s", __func__, ssh_err(r));
}
static char * static char *
list_hostkey_types(void) list_hostkey_types(void)
{ {
Buffer b; struct sshbuf *b;
const char *p; struct sshkey *key;
char *ret; char *ret;
u_int i; u_int i;
struct sshkey *key;
buffer_init(&b); if ((b = sshbuf_new()) == NULL)
fatal("%s: sshbuf_new failed", __func__);
for (i = 0; i < options.num_host_key_files; i++) { for (i = 0; i < options.num_host_key_files; i++) {
key = sensitive_data.host_keys[i]; key = sensitive_data.host_keys[i];
if (key == NULL) if (key == NULL)
key = sensitive_data.host_pubkeys[i]; key = sensitive_data.host_pubkeys[i];
if (key == NULL) if (key == NULL)
continue; continue;
/* Check that the key is accepted in HostkeyAlgorithms */
if (match_pattern_list(sshkey_ssh_name(key),
options.hostkeyalgorithms, 0) != 1) {
debug3("%s: %s key not permitted by HostkeyAlgorithms",
__func__, sshkey_ssh_name(key));
continue;
}
switch (key->type) { switch (key->type) {
case KEY_RSA: case KEY_RSA:
/* for RSA we also support SHA2 signatures */
append_hostkey_type(b, "rsa-sha2-512");
append_hostkey_type(b, "rsa-sha2-256");
/* FALLTHROUGH */
case KEY_DSA: case KEY_DSA:
case KEY_ECDSA: case KEY_ECDSA:
case KEY_ED25519: case KEY_ED25519:
case KEY_XMSS: case KEY_XMSS:
if (buffer_len(&b) > 0) append_hostkey_type(b, sshkey_ssh_name(key));
buffer_append(&b, ",", 1);
p = key_ssh_name(key);
buffer_append(&b, p, strlen(p));
/* for RSA we also support SHA2 signatures */
if (key->type == KEY_RSA) {
p = ",rsa-sha2-512,rsa-sha2-256";
buffer_append(&b, p, strlen(p));
}
break; break;
} }
/* If the private key has a cert peer, then list that too */ /* If the private key has a cert peer, then list that too */
@ -728,21 +730,24 @@ list_hostkey_types(void)
continue; continue;
switch (key->type) { switch (key->type) {
case KEY_RSA_CERT: case KEY_RSA_CERT:
/* for RSA we also support SHA2 signatures */
append_hostkey_type(b,
"rsa-sha2-512-cert-v01@openssh.com");
append_hostkey_type(b,
"rsa-sha2-256-cert-v01@openssh.com");
/* FALLTHROUGH */
case KEY_DSA_CERT: case KEY_DSA_CERT:
case KEY_ECDSA_CERT: case KEY_ECDSA_CERT:
case KEY_ED25519_CERT: case KEY_ED25519_CERT:
case KEY_XMSS_CERT: case KEY_XMSS_CERT:
if (buffer_len(&b) > 0) append_hostkey_type(b, sshkey_ssh_name(key));
buffer_append(&b, ",", 1);
p = key_ssh_name(key);
buffer_append(&b, p, strlen(p));
break; break;
} }
} }
if ((ret = sshbuf_dup_string(&b)) == NULL) if ((ret = sshbuf_dup_string(b)) == NULL)
fatal("%s: sshbuf_dup_string failed", __func__); fatal("%s: sshbuf_dup_string failed", __func__);
buffer_free(&b); sshbuf_free(b);
debug("list_hostkey_types: %s", ret); debug("%s: %s", __func__, ret);
return ret; return ret;
} }

View File

@ -33,7 +33,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\" .\"
.\" $OpenBSD: sshd_config.5,v 1.278 2018/07/03 10:59:35 djm Exp $ .\" $OpenBSD: sshd_config.5,v 1.279 2018/07/03 11:39:54 djm Exp $
.Dd $Mdocdate: July 3 2018 $ .Dd $Mdocdate: July 3 2018 $
.Dt SSHD_CONFIG 5 .Dt SSHD_CONFIG 5
.Os .Os
@ -674,9 +674,10 @@ ecdsa-sha2-nistp256-cert-v01@openssh.com,
ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com,
ecdsa-sha2-nistp521-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com,
ssh-ed25519-cert-v01@openssh.com, ssh-ed25519-cert-v01@openssh.com,
rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,
ssh-rsa-cert-v01@openssh.com, ssh-rsa-cert-v01@openssh.com,
ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
ssh-ed25519,ssh-rsa ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
.Ed .Ed
.Pp .Pp
The list of available key types may also be obtained using The list of available key types may also be obtained using
@ -751,9 +752,10 @@ ecdsa-sha2-nistp256-cert-v01@openssh.com,
ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com,
ecdsa-sha2-nistp521-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com,
ssh-ed25519-cert-v01@openssh.com, ssh-ed25519-cert-v01@openssh.com,
rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,
ssh-rsa-cert-v01@openssh.com, ssh-rsa-cert-v01@openssh.com,
ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
ssh-ed25519,ssh-rsa ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
.Ed .Ed
.Pp .Pp
The list of available key types may also be obtained using The list of available key types may also be obtained using
@ -1399,9 +1401,10 @@ ecdsa-sha2-nistp256-cert-v01@openssh.com,
ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com,
ecdsa-sha2-nistp521-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com,
ssh-ed25519-cert-v01@openssh.com, ssh-ed25519-cert-v01@openssh.com,
rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,
ssh-rsa-cert-v01@openssh.com, ssh-rsa-cert-v01@openssh.com,
ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
ssh-ed25519,ssh-rsa ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
.Ed .Ed
.Pp .Pp
The list of available key types may also be obtained using The list of available key types may also be obtained using

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssherr.c,v 1.7 2017/09/12 06:32:08 djm Exp $ */ /* $OpenBSD: ssherr.c,v 1.8 2018/07/03 11:39:54 djm Exp $ */
/* /*
* Copyright (c) 2011 Damien Miller * Copyright (c) 2011 Damien Miller
* *
@ -139,6 +139,8 @@ ssh_err(int n)
return "Invalid key length"; return "Invalid key length";
case SSH_ERR_NUMBER_TOO_LARGE: case SSH_ERR_NUMBER_TOO_LARGE:
return "number is too large"; return "number is too large";
case SSH_ERR_SIGN_ALG_UNSUPPORTED:
return "signature algorithm not supported";
default: default:
return "unknown error"; return "unknown error";
} }

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssherr.h,v 1.5 2017/09/12 06:32:08 djm Exp $ */ /* $OpenBSD: ssherr.h,v 1.6 2018/07/03 11:39:54 djm Exp $ */
/* /*
* Copyright (c) 2011 Damien Miller * Copyright (c) 2011 Damien Miller
* *
@ -79,6 +79,7 @@
#define SSH_ERR_PROTOCOL_ERROR -55 #define SSH_ERR_PROTOCOL_ERROR -55
#define SSH_ERR_KEY_LENGTH -56 #define SSH_ERR_KEY_LENGTH -56
#define SSH_ERR_NUMBER_TOO_LARGE -57 #define SSH_ERR_NUMBER_TOO_LARGE -57
#define SSH_ERR_SIGN_ALG_UNSUPPORTED -58
/* Translate a numeric error code to a human-readable error string */ /* Translate a numeric error code to a human-readable error string */
const char *ssh_err(int n); const char *ssh_err(int n);

104
sshkey.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshkey.c,v 1.64 2018/03/22 07:05:48 markus Exp $ */ /* $OpenBSD: sshkey.c,v 1.65 2018/07/03 11:39:54 djm Exp $ */
/* /*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Alexander von Gernler. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved.
@ -83,46 +83,64 @@ static int sshkey_from_blob_internal(struct sshbuf *buf,
struct keytype { struct keytype {
const char *name; const char *name;
const char *shortname; const char *shortname;
const char *sigalg;
int type; int type;
int nid; int nid;
int cert; int cert;
int sigonly; int sigonly;
}; };
static const struct keytype keytypes[] = { static const struct keytype keytypes[] = {
{ "ssh-ed25519", "ED25519", KEY_ED25519, 0, 0, 0 }, { "ssh-ed25519", "ED25519", NULL, KEY_ED25519, 0, 0, 0 },
{ "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT", { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT", NULL,
KEY_ED25519_CERT, 0, 1, 0 }, KEY_ED25519_CERT, 0, 1, 0 },
#ifdef WITH_XMSS #ifdef WITH_XMSS
{ "ssh-xmss@openssh.com", "XMSS", KEY_XMSS, 0, 0, 0 }, { "ssh-xmss@openssh.com", "XMSS", NULL, KEY_XMSS, 0, 0, 0 },
{ "ssh-xmss-cert-v01@openssh.com", "XMSS-CERT", { "ssh-xmss-cert-v01@openssh.com", "XMSS-CERT", NULL,
KEY_XMSS_CERT, 0, 1, 0 }, KEY_XMSS_CERT, 0, 1, 0 },
#endif /* WITH_XMSS */ #endif /* WITH_XMSS */
#ifdef WITH_OPENSSL #ifdef WITH_OPENSSL
{ "ssh-rsa", "RSA", KEY_RSA, 0, 0, 0 }, { "ssh-rsa", "RSA", NULL, KEY_RSA, 0, 0, 0 },
{ "rsa-sha2-256", "RSA", KEY_RSA, 0, 0, 1 }, { "rsa-sha2-256", "RSA", NULL, KEY_RSA, 0, 0, 1 },
{ "rsa-sha2-512", "RSA", KEY_RSA, 0, 0, 1 }, { "rsa-sha2-512", "RSA", NULL, KEY_RSA, 0, 0, 1 },
{ "ssh-dss", "DSA", KEY_DSA, 0, 0, 0 }, { "ssh-dss", "DSA", NULL, KEY_DSA, 0, 0, 0 },
# ifdef OPENSSL_HAS_ECC # ifdef OPENSSL_HAS_ECC
{ "ecdsa-sha2-nistp256", "ECDSA", KEY_ECDSA, NID_X9_62_prime256v1, 0, 0 }, { "ecdsa-sha2-nistp256", "ECDSA", NULL,
{ "ecdsa-sha2-nistp384", "ECDSA", KEY_ECDSA, NID_secp384r1, 0, 0 }, KEY_ECDSA, NID_X9_62_prime256v1, 0, 0 },
{ "ecdsa-sha2-nistp384", "ECDSA", NULL,
KEY_ECDSA, NID_secp384r1, 0, 0 },
# ifdef OPENSSL_HAS_NISTP521 # ifdef OPENSSL_HAS_NISTP521
{ "ecdsa-sha2-nistp521", "ECDSA", KEY_ECDSA, NID_secp521r1, 0, 0 }, { "ecdsa-sha2-nistp521", "ECDSA", NULL,
KEY_ECDSA, NID_secp521r1, 0, 0 },
# endif /* OPENSSL_HAS_NISTP521 */ # endif /* OPENSSL_HAS_NISTP521 */
# endif /* OPENSSL_HAS_ECC */ # endif /* OPENSSL_HAS_ECC */
{ "ssh-rsa-cert-v01@openssh.com", "RSA-CERT", KEY_RSA_CERT, 0, 1, 0 }, { "ssh-rsa-cert-v01@openssh.com", "RSA-CERT", NULL,
{ "ssh-dss-cert-v01@openssh.com", "DSA-CERT", KEY_DSA_CERT, 0, 1, 0 }, KEY_RSA_CERT, 0, 1, 0 },
{ "rsa-sha2-256-cert-v01@openssh.com", "RSA-CERT",
"ssh-rsa-sha2-256", KEY_RSA_CERT, 0, 1, 1 },
{ "rsa-sha2-512-cert-v01@openssh.com", "RSA-CERT",
"ssh-rsa-sha2-512", KEY_RSA_CERT, 0, 1, 1 },
{ "ssh-dss-cert-v01@openssh.com", "DSA-CERT", NULL,
KEY_DSA_CERT, 0, 1, 0 },
{ "ssh-rsa-cert-v01@openssh.com", "RSA-CERT", NULL,
KEY_RSA_CERT, 0, 1, 0 },
{ "rsa-sha2-256-cert-v01@openssh.com", "RSA-CERT",
"ssh-rsa-sha2-256", KEY_RSA_CERT, 0, 1, 1 },
{ "rsa-sha2-512-cert-v01@openssh.com", "RSA-CERT",
"ssh-rsa-sha2-512", KEY_RSA_CERT, 0, 1, 1 },
{ "ssh-dss-cert-v01@openssh.com", "DSA-CERT", NULL,
KEY_DSA_CERT, 0, 1, 0 },
# ifdef OPENSSL_HAS_ECC # ifdef OPENSSL_HAS_ECC
{ "ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA-CERT", { "ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA-CERT", NULL,
KEY_ECDSA_CERT, NID_X9_62_prime256v1, 1, 0 }, KEY_ECDSA_CERT, NID_X9_62_prime256v1, 1, 0 },
{ "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA-CERT", { "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA-CERT", NULL,
KEY_ECDSA_CERT, NID_secp384r1, 1, 0 }, KEY_ECDSA_CERT, NID_secp384r1, 1, 0 },
# ifdef OPENSSL_HAS_NISTP521 # ifdef OPENSSL_HAS_NISTP521
{ "ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA-CERT", { "ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA-CERT", NULL,
KEY_ECDSA_CERT, NID_secp521r1, 1, 0 }, KEY_ECDSA_CERT, NID_secp521r1, 1, 0 },
# endif /* OPENSSL_HAS_NISTP521 */ # endif /* OPENSSL_HAS_NISTP521 */
# endif /* OPENSSL_HAS_ECC */ # endif /* OPENSSL_HAS_ECC */
#endif /* WITH_OPENSSL */ #endif /* WITH_OPENSSL */
{ NULL, NULL, -1, -1, 0, 0 } { NULL, NULL, NULL, -1, -1, 0, 0 }
}; };
const char * const char *
@ -2198,8 +2216,8 @@ sshkey_froms(struct sshbuf *buf, struct sshkey **keyp)
return r; return r;
} }
int static int
sshkey_sigtype(const u_char *sig, size_t siglen, char **sigtypep) get_sigtype(const u_char *sig, size_t siglen, char **sigtypep)
{ {
int r; int r;
struct sshbuf *b = NULL; struct sshbuf *b = NULL;
@ -2223,6 +2241,50 @@ sshkey_sigtype(const u_char *sig, size_t siglen, char **sigtypep)
return r; return r;
} }
/*
* Returns the expected signature algorithm for a given public key algorithm.
*/
static const char *
sigalg_by_name(const char *name)
{
const struct keytype *kt;
for (kt = keytypes; kt->type != -1; kt++) {
if (strcmp(kt->name, name) != 0)
continue;
if (kt->sigalg != NULL)
return kt->sigalg;
if (!kt->cert)
return kt->name;
return sshkey_ssh_name_from_type_nid(
sshkey_type_plain(kt->type), kt->nid);
}
return NULL;
}
/*
* Verifies that the signature algorithm appearing inside the signature blob
* matches that which was requested.
*/
int
sshkey_check_sigtype(const u_char *sig, size_t siglen,
const char *requested_alg)
{
const char *expected_alg;
char *sigtype = NULL;
int r;
if (requested_alg == NULL)
return 0;
if ((expected_alg = sigalg_by_name(requested_alg)) == NULL)
return SSH_ERR_INVALID_ARGUMENT;
if ((r = get_sigtype(sig, siglen, &sigtype)) != 0)
return r;
r = strcmp(expected_alg, sigtype) == 0;
free(sigtype);
return r ? 0 : SSH_ERR_SIGN_ALG_UNSUPPORTED;
}
int int
sshkey_sign(const struct sshkey *key, sshkey_sign(const struct sshkey *key,
u_char **sigp, size_t *lenp, u_char **sigp, size_t *lenp,

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshkey.h,v 1.24 2018/02/23 15:58:38 markus Exp $ */ /* $OpenBSD: sshkey.h,v 1.25 2018/07/03 11:39:54 djm Exp $ */
/* /*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@ -191,11 +191,11 @@ int sshkey_puts_opts(const struct sshkey *, struct sshbuf *,
int sshkey_plain_to_blob(const struct sshkey *, u_char **, size_t *); int sshkey_plain_to_blob(const struct sshkey *, u_char **, size_t *);
int sshkey_putb_plain(const struct sshkey *, struct sshbuf *); int sshkey_putb_plain(const struct sshkey *, struct sshbuf *);
int sshkey_sigtype(const u_char *, size_t, char **);
int sshkey_sign(const struct sshkey *, u_char **, size_t *, int sshkey_sign(const struct sshkey *, u_char **, size_t *,
const u_char *, size_t, const char *, u_int); const u_char *, size_t, const char *, u_int);
int sshkey_verify(const struct sshkey *, const u_char *, size_t, int sshkey_verify(const struct sshkey *, const u_char *, size_t,
const u_char *, size_t, const char *, u_int); const u_char *, size_t, const char *, u_int);
int sshkey_check_sigtype(const u_char *, size_t, const char *);
/* for debug */ /* for debug */
void sshkey_dump_ec_point(const EC_GROUP *, const EC_POINT *); void sshkey_dump_ec_point(const EC_GROUP *, const EC_POINT *);