summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--PROTOCOL.certkeys20
-rw-r--r--auth2-hostbased.c5
-rw-r--r--auth2-pubkey.c13
-rw-r--r--authfd.c24
-rw-r--r--compat.c27
-rw-r--r--compat.h4
-rw-r--r--kex.c17
-rw-r--r--kex.h4
-rw-r--r--myproposal.h4
-rw-r--r--ssh-rsa.c60
-rw-r--r--ssh_config.513
-rw-r--r--sshconnect2.c345
-rw-r--r--sshd.c63
-rw-r--r--sshd_config.511
-rw-r--r--ssherr.c4
-rw-r--r--ssherr.h3
-rw-r--r--sshkey.c104
-rw-r--r--sshkey.h4
18 files changed, 469 insertions, 256 deletions
diff --git a/PROTOCOL.certkeys b/PROTOCOL.certkeys
index 65f11f53..11363fdc 100644
--- a/PROTOCOL.certkeys
+++ b/PROTOCOL.certkeys
@@ -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
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:
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-nistp521-cert-v01@openssh.com
-These include certification information along with the public key
-that is used to sign challenges. ssh-keygen performs the CA signing
-operation.
+Two additional types exist for RSA certificates to force use of
+SHA-2 signatures (SHA-256 and SHA-512 respectively):
+
+ 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
-------------------
@@ -291,4 +303,4 @@ permit-user-rc empty Flag indicating that execution of
of this script will not be permitted if
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 $
diff --git a/auth2-hostbased.c b/auth2-hostbased.c
index 8996f7e0..f70609cb 100644
--- a/auth2-hostbased.c
+++ b/auth2-hostbased.c
@@ -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.
*
@@ -111,8 +111,7 @@ userauth_hostbased(struct ssh *ssh)
"signature format");
goto done;
}
- if (match_pattern_list(sshkey_ssh_name(key),
- options.hostbased_key_types, 0) != 1) {
+ if (match_pattern_list(pkalg, options.hostbased_key_types, 0) != 1) {
logit("%s: key type %s not in HostbasedAcceptedKeyTypes",
__func__, sshkey_type(key));
goto done;
diff --git a/auth2-pubkey.c b/auth2-pubkey.c
index 3ccc3a21..4feeae3e 100644
--- a/auth2-pubkey.c
+++ b/auth2-pubkey.c
@@ -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.
*
@@ -109,7 +109,7 @@ userauth_pubkey(struct ssh *ssh)
pktype = sshkey_type_from_name(pkalg);
if (pktype == KEY_UNSPEC) {
/* this is perfectly legal */
- logit("%s: unsupported public key algorithm: %s",
+ verbose("%s: unsupported public key algorithm: %s",
__func__, pkalg);
goto done;
}
@@ -136,8 +136,7 @@ userauth_pubkey(struct ssh *ssh)
logit("refusing previously-used %s key", sshkey_type(key));
goto done;
}
- if (match_pattern_list(sshkey_ssh_name(key),
- options.pubkey_key_types, 0) != 1) {
+ if (match_pattern_list(pkalg, options.pubkey_key_types, 0) != 1) {
logit("%s: key type %s not in PubkeyAcceptedKeyTypes",
__func__, sshkey_ssh_name(key));
goto done;
@@ -188,8 +187,10 @@ userauth_pubkey(struct ssh *ssh)
/* test for correct signature */
authenticated = 0;
if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) &&
- PRIVSEP(sshkey_verify(key, sig, slen, sshbuf_ptr(b),
- sshbuf_len(b), NULL, ssh->compat)) == 0) {
+ PRIVSEP(sshkey_verify(key, sig, slen,
+ sshbuf_ptr(b), sshbuf_len(b),
+ (ssh->compat & SSH_BUG_SIGTYPE) == 0 ? pkalg : NULL,
+ ssh->compat)) == 0) {
authenticated = 1;
}
sshbuf_free(b);
diff --git a/authfd.c b/authfd.c
index 3ee7dffa..f24230b7 100644
--- a/authfd.c
+++ b/authfd.c
@@ -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>
* 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)
{
struct sshbuf *msg;
- u_char *blob = NULL, type;
- size_t blen = 0, len = 0;
+ u_char *sig = NULL, type = 0;
+ size_t len = 0;
u_int flags = 0;
int r = SSH_ERR_INTERNAL_ERROR;
@@ -355,11 +355,9 @@ ssh_agent_sign(int sock, const struct sshkey *key,
return SSH_ERR_INVALID_ARGUMENT;
if ((msg = sshbuf_new()) == NULL)
return SSH_ERR_ALLOC_FAIL;
- if ((r = sshkey_to_blob(key, &blob, &blen)) != 0)
- goto out;
flags |= agent_encode_alg(key, alg);
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_u32(msg, flags)) != 0)
goto out;
@@ -374,15 +372,19 @@ ssh_agent_sign(int sock, const struct sshkey *key,
r = SSH_ERR_INVALID_FORMAT;
goto out;
}
- if ((r = sshbuf_get_string(msg, sigp, &len)) != 0)
+ if ((r = sshbuf_get_string(msg, &sig, &len)) != 0)
+ 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;
+ sig = NULL;
+ len = 0;
r = 0;
out:
- if (blob != NULL) {
- explicit_bzero(blob, blen);
- free(blob);
- }
+ freezero(sig, len);
sshbuf_free(msg);
return r;
}
diff --git a/compat.c b/compat.c
index 1c0e0873..1c9890aa 100644
--- a/compat.c
+++ b/compat.c
@@ -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.
*
@@ -52,16 +52,27 @@ compat_datafellows(const char *version)
} check[] = {
{ "OpenSSH_2.*,"
"OpenSSH_3.0*,"
- "OpenSSH_3.1*", SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR},
- { "OpenSSH_3.*", SSH_OLD_FORWARD_ADDR },
- { "Sun_SSH_1.0*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF},
+ "OpenSSH_3.1*", SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR|
+ SSH_BUG_SIGTYPE},
+ { "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_3*,"
- "OpenSSH_4*", 0 },
- { "OpenSSH_5*", SSH_NEW_OPENSSH|SSH_BUG_DYNAMIC_RPORT},
- { "OpenSSH_6.6.1*", SSH_NEW_OPENSSH},
+ "OpenSSH_4*", SSH_BUG_SIGTYPE },
+ { "OpenSSH_5*", SSH_NEW_OPENSSH|SSH_BUG_DYNAMIC_RPORT|
+ SSH_BUG_SIGTYPE},
+ { "OpenSSH_6.6.1*", SSH_NEW_OPENSSH|SSH_BUG_SIGTYPE},
{ "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 },
{ "*MindTerm*", 0 },
{ "3.0.*", SSH_BUG_DEBUG },
diff --git a/compat.h b/compat.h
index 4fee3495..28d2c813 100644
--- a/compat.h
+++ b/compat.h
@@ -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.
@@ -33,7 +33,7 @@
#define SSH_PROTO_2 0x04
#define SSH_BUG_UTF8TTYMODE 0x00000001
-/* #define unused 0x00000002 */
+#define SSH_BUG_SIGTYPE 0x00000002
/* #define unused 0x00000004 */
/* #define unused 0x00000008 */
#define SSH_OLD_SESSIONID 0x00000010
diff --git a/kex.c b/kex.c
index 15ea28b0..d0a5f1b6 100644
--- a/kex.c
+++ b/kex.c
@@ -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.
*
@@ -342,6 +342,7 @@ kex_send_ext_info(struct ssh *ssh)
if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
return SSH_ERR_ALLOC_FAIL;
+ /* XXX filter algs list by allowed pubkey/hostbased types */
if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
(r = sshpkt_put_u32(ssh, 1)) != 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;
u_int32_t i, ninfo;
- char *name, *found;
+ char *name;
u_char *val;
size_t vlen;
int r;
@@ -401,16 +402,8 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
return SSH_ERR_INVALID_FORMAT;
}
debug("%s: %s=<%s>", __func__, name, val);
- found = match_list("rsa-sha2-256", val, NULL);
- if (found) {
- kex->rsa_sha2 = 256;
- free(found);
- }
- found = match_list("rsa-sha2-512", val, NULL);
- if (found) {
- kex->rsa_sha2 = 512;
- free(found);
- }
+ kex->server_sig_algs = val;
+ val = NULL;
} else
debug("%s: %s (unrecognised)", __func__, name);
free(name);
diff --git a/kex.h b/kex.h
index 01bb3986..6210630d 100644
--- a/kex.h
+++ b/kex.h
@@ -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.
@@ -139,7 +139,7 @@ struct kex {
int hostkey_type;
int hostkey_nid;
u_int kex_type;
- int rsa_sha2;
+ char *server_sig_algs;
int ext_info_c;
struct sshbuf *my;
struct sshbuf *peer;
diff --git a/myproposal.h b/myproposal.h
index c255147a..08782dd3 100644
--- a/myproposal.h
+++ b/myproposal.h
@@ -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.
@@ -107,6 +107,8 @@
#define KEX_DEFAULT_PK_ALG \
HOSTKEY_ECDSA_CERT_METHODS \
"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," \
HOSTKEY_ECDSA_METHODS \
"ssh-ed25519," \
diff --git a/ssh-rsa.c b/ssh-rsa.c
index 49e71c87..1756315b 100644
--- a/ssh-rsa.c
+++ b/ssh-rsa.c
@@ -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>
*
@@ -51,11 +51,14 @@ rsa_hash_alg_ident(int hash_alg)
return NULL;
}
+/*
+ * Returns the hash algorithm ID for a given algorithm identifier as used
+ * inside the signature blob,
+ */
static int
-rsa_hash_alg_from_ident(const char *ident)
+rsa_hash_id_from_ident(const char *ident)
{
- if (strcmp(ident, "ssh-rsa") == 0 ||
- strcmp(ident, "ssh-rsa-cert-v01@openssh.com") == 0)
+ if (strcmp(ident, "ssh-rsa") == 0)
return SSH_DIGEST_SHA1;
if (strcmp(ident, "rsa-sha2-256") == 0)
return SSH_DIGEST_SHA256;
@@ -64,6 +67,27 @@ rsa_hash_alg_from_ident(const char *ident)
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
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)
hash_alg = SSH_DIGEST_SHA1;
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 ||
sshkey_type_plain(key->type) != KEY_RSA)
return SSH_ERR_INVALID_ARGUMENT;
@@ -202,7 +226,7 @@ ssh_rsa_verify(const struct sshkey *key,
const char *alg)
{
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;
struct sshbuf *b = 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;
goto out;
}
- /* XXX djm: need cert types that reliably yield SHA-2 signatures */
- 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) {
+ if ((hash_alg = rsa_hash_id_from_ident(sigtype)) == -1) {
ret = SSH_ERR_KEY_TYPE_MISMATCH;
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) {
ret = SSH_ERR_INVALID_FORMAT;
goto out;
diff --git a/ssh_config.5 b/ssh_config.5
index e5eadcaa..eff9c5e6 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" 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 $
-.Dd $Mdocdate: June 9 2018 $
+.\" $OpenBSD: ssh_config.5,v 1.278 2018/07/03 11:39:54 djm Exp $
+.Dd $Mdocdate: July 3 2018 $
.Dt SSH_CONFIG 5
.Os
.Sh NAME
@@ -772,9 +772,10 @@ ecdsa-sha2-nistp256-cert-v01@openssh.com,
ecdsa-sha2-nistp384-cert-v01@openssh.com,
ecdsa-sha2-nistp521-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,
ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
-ssh-ed25519,ssh-rsa
+ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
.Ed
.Pp
The
@@ -799,9 +800,10 @@ ecdsa-sha2-nistp256-cert-v01@openssh.com,
ecdsa-sha2-nistp384-cert-v01@openssh.com,
ecdsa-sha2-nistp521-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,
ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
-ssh-ed25519,ssh-rsa
+ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
.Ed
.Pp
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-nistp521-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,
ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
-ssh-ed25519,ssh-rsa
+ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
.Ed
.Pp
The list of available key types may also be obtained using
diff --git a/sshconnect2.c b/sshconnect2.c
index d8ae6eb3..92037640 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -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) 2008 Damien Miller. All rights reserved.
@@ -315,7 +315,7 @@ int input_gssapi_errtok(int, u_int32_t, struct ssh *);
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_cleanup(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) {
if (key_equal(key, id->key)) {
- sent = sign_and_send_pubkey(authctxt, id);
+ sent = sign_and_send_pubkey(ssh, authctxt, id);
break;
}
}
@@ -986,73 +986,80 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, struct ssh *ssh)
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
- * rsa-sha2-* signature. Check what they actually gave back and warn the
- * user if the agent has returned an unexpected type.
+ * Select an algorithm for publickey signatures.
+ * Returns algorithm (caller must free) or NULL if no mutual algorithm found.
+ *
+ * Call with ssh==NULL to ignore server-sig-algs extension list and
+ * only attempt with the key's base signature type.
*/
-static int
-check_sigtype(const struct sshkey *key, const u_char *sig, size_t len)
+static char *
+key_sig_algorithm(struct ssh *ssh, const struct sshkey *key)
{
- int r;
- char *sigtype = NULL;
- const char *alg = key_sign_encode(key);
+ char *allowed, *oallowed, *cp, *alg = NULL;
- if (sshkey_is_cert(key))
- return 0;
- if ((r = sshkey_sigtype(sig, len, &sigtype)) != 0)
- return r;
- if (strcmp(sigtype, alg) != 0) {
- logit("warning: agent returned different signature type %s "
- "(expected %s)", sigtype, alg);
- }
- free(sigtype);
- /* Incorrect signature types aren't an error ... yet */
- return 0;
+ /*
+ * The signature algorithm will only differ from the key algorithm
+ * for RSA keys/certs and when the server advertises support for
+ * newer (SHA2) algorithms.
+ */
+ if (ssh == NULL || ssh->kex->server_sig_algs == NULL ||
+ (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);
+ }
+
+ /*
+ * 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
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;
int r;
- /* the agent supports this key */
+ /* The agent supports this key. */
if (id->key != NULL && id->agent_fd != -1) {
- if ((r = ssh_agent_sign(id->agent_fd, id->key, sigp, lenp,
- data, datalen, key_sign_encode(id->key), compat)) != 0 ||
- (r = check_sigtype(id->key, *sigp, *lenp)) != 0)
- return r;
- return 0;
+ return ssh_agent_sign(id->agent_fd, id->key, sigp, lenp,
+ data, datalen, alg, compat);
}
/*
- * we have already loaded the private key or
- * the private key is stored in external hardware
+ * 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));
+ (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 0;
+ }
- /* load the private key from the file */
+ /* Load the private key from the file. */
if ((prv = load_identity_file(id)) == NULL)
return SSH_ERR_KEY_NOT_FOUND;
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);
return SSH_ERR_KEY_NOT_FOUND;
}
- r = sshkey_sign(prv, sigp, lenp, data, datalen,
- key_sign_encode(prv), compat);
+ r = sshkey_sign(prv, sigp, lenp, data, datalen, alg, compat);
sshkey_free(prv);
return r;
}
@@ -1086,57 +1092,35 @@ id_filename_matches(Identity *id, Identity *private_id)
}
static int
-sign_and_send_pubkey(Authctxt *authctxt, Identity *id)
+sign_and_send_pubkey(struct ssh *ssh, Authctxt *authctxt, Identity *id)
{
- Buffer b;
- Identity *private_id;
- u_char *blob, *signature;
- size_t slen;
- u_int bloblen, skip = 0;
- int matched, ret = -1, have_sig = 1;
- char *fp;
+ struct sshbuf *b = NULL;
+ Identity *private_id, *sign_id = NULL;
+ u_char *signature = NULL;
+ size_t slen = 0, skip = 0;
+ int r, fallback_sigtype, sent = 0;
+ char *alg = NULL, *fp = NULL;
+ const char *loc = "";
if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash,
SSH_FP_DEFAULT)) == NULL)
return 0;
- debug3("%s: %s %s", __func__, key_type(id->key), fp);
- free(fp);
- if (key_to_blob(id->key, &blob, &bloblen) == 0) {
- /* 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);
+ debug3("%s: %s %s", __func__, sshkey_type(id->key), fp);
/*
* If the key is an certificate, try to find a matching private key
* and use it to complete the signature.
* If no such private key exists, fall back to trying the certificate
* 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)) {
- matched = 0;
+ if (sshkey_is_cert(id->key)) {
TAILQ_FOREACH(private_id, &authctxt->keys, next) {
if (sshkey_equal_public(id->key, private_id->key) &&
id->key->type != private_id->key->type) {
- id = private_id;
- matched = 1;
+ sign_id = private_id;
break;
}
}
@@ -1147,18 +1131,18 @@ sign_and_send_pubkey(Authctxt *authctxt, Identity *id)
* of keeping just a private key file and public
* 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) {
TAILQ_FOREACH(private_id, &authctxt->keys, next) {
if (private_id->key == NULL &&
id_filename_matches(id, private_id)) {
- id = private_id;
- matched = 1;
+ sign_id = private_id;
break;
}
}
}
- if (matched) {
+ if (sign_id != NULL) {
debug2("%s: using private key \"%s\"%s for "
"certificate", __func__, id->filename,
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,
- buffer_ptr(&b), buffer_len(&b), datafellows);
- if (ret != 0) {
- if (ret != SSH_ERR_KEY_NOT_FOUND)
- error("%s: signing failed: %s", __func__, ssh_err(ret));
- free(blob);
- buffer_free(&b);
- return 0;
+ /*
+ * If the above didn't select another identity to do the signing
+ * then default to the one we started with.
+ */
+ if (sign_id == NULL)
+ sign_id = id;
+
+ /* assemble and sign data */
+ 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
- buffer_dump(&b);
-#endif
- free(blob);
+ if (slen == 0 || signature == NULL) /* shouldn't happen */
+ fatal("%s: no signature", __func__);
/* append signature */
- buffer_put_string(&b, signature, slen);
- free(signature);
+ if ((r = sshbuf_put_string(b, signature, slen)) != 0)
+ fatal("%s: append signature: %s", __func__, ssh_err(r));
+#ifdef DEBUG_PK
+ sshbuf_dump(b, stderr);
+#endif
/* skip session id and packet type */
- if (buffer_len(&b) < skip + 1)
- fatal("userauth_pubkey: internal error");
- buffer_consume(&b, skip + 1);
+ if ((r = sshbuf_consume(b, skip + 1)) != 0)
+ fatal("%s: consume: %s", __func__, ssh_err(r));
/* put remaining data from buffer into packet */
- packet_start(SSH2_MSG_USERAUTH_REQUEST);
- packet_put_raw(buffer_ptr(&b), buffer_len(&b));
- buffer_free(&b);
- packet_send();
+ if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
+ (r = sshpkt_putb(ssh, b)) != 0 ||
+ (r = sshpkt_send(ssh)) != 0)
+ 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
-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;
+ 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) {
/* we cannot handle this key */
- debug3("send_pubkey_test: cannot handle key");
- return 0;
+ debug3("%s: cannot handle key", __func__);
+ goto out;
}
/* register callback for USERAUTH_PK_OK message */
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->method->name);
packet_put_char(have_sig);
- packet_put_cstring(key_sign_encode(id->key));
+ packet_put_cstring(alg);
packet_put_string(blob, bloblen);
- free(blob);
packet_send();
- return 1;
+ /* success */
+ sent = 1;
+out:
+ free(alg);
+ free(blob);
+ return