mirror of
https://github.com/openssh/openssh-portable.git
synced 2026-11-06 05:16:43 +00:00
upstream: Check key and CA sig type during key parsing
Checks key type and CA signature algorithm allowlists as early as possible during public key deserialisation. Use this in the client and server to reduce attack surface from disallowed key/signature types. With Chris Rohlf in collaboration with Claude and Anthropic Research ok markus, deraadt OpenBSD-Commit-ID: b03cb3755506231a742eed844f269524812f560a
This commit is contained in:
1 parent
813f670ccc
commit
65666f4e82
9 files changed
+170
-94
No files matched your search
+28
-21
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: auth2-hostbased.c,v 1.58 2026/07/30 03:37:39 djm Exp $ */
|
||||
/* $OpenBSD: auth2-hostbased.c,v 1.59 2026/09/16 00:31:27 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
*
|
||||
@@ -60,16 +60,15 @@ static int
|
||||
userauth_hostbased(struct ssh *ssh, const char *method)
|
||||
{
|
||||
Authctxt *authctxt = ssh->authctxt;
|
||||
struct sshbuf *b;
|
||||
struct sshbuf *keyblob = NULL, *b = NULL;
|
||||
struct sshkey *key = NULL;
|
||||
char *pkalg, *cuser, *chost;
|
||||
u_char *pkblob, *sig;
|
||||
size_t alen, blen, slen;
|
||||
u_char *sig;
|
||||
size_t alen, slen;
|
||||
int r, pktype, authenticated = 0;
|
||||
|
||||
/* XXX use sshkey_froms() */
|
||||
if ((r = sshpkt_get_cstring(ssh, &pkalg, &alen)) != 0 ||
|
||||
(r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0 ||
|
||||
(r = sshpkt_getb_froms(ssh, &keyblob)) != 0 ||
|
||||
(r = sshpkt_get_cstring(ssh, &chost, NULL)) != 0 ||
|
||||
(r = sshpkt_get_cstring(ssh, &cuser, NULL)) != 0 ||
|
||||
(r = sshpkt_get_string(ssh, &sig, &slen)) != 0)
|
||||
@@ -94,27 +93,35 @@ userauth_hostbased(struct ssh *ssh, const char *method)
|
||||
"HostbasedAcceptedAlgorithms", pkalg);
|
||||
goto done;
|
||||
}
|
||||
if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
|
||||
error_fr(r, "key_from_blob");
|
||||
if ((b = sshbuf_fromb(keyblob)) == NULL)
|
||||
fatal_f("sshbuf_fromb failed");
|
||||
switch ((r = sshkey_fromb_allowlist(b, &key,
|
||||
options.hostbased_accepted_algos, options.ca_sign_algorithms))) {
|
||||
case 0:
|
||||
/* ok */
|
||||
break;
|
||||
case SSH_ERR_KEY_ALG_UNSUPPORTED:
|
||||
/* This shouldn't happen unless the client is being weird */
|
||||
logit_f("key algorithm differs from signature algorithm %s and "
|
||||
"is not in HostbasedAcceptedAlgorithms", pkalg);
|
||||
goto done;
|
||||
}
|
||||
if (key == NULL) {
|
||||
error_f("cannot decode key: %s", pkalg);
|
||||
case SSH_ERR_SIGN_ALG_UNSUPPORTED:
|
||||
logit_fr(r, "certificate signature algorithm not in "
|
||||
"CASignatureAlgorithms");
|
||||
goto done;
|
||||
default:
|
||||
error_fr(r, "parse key");
|
||||
goto done;
|
||||
}
|
||||
sshbuf_free(b);
|
||||
b = NULL;
|
||||
|
||||
if (key->type != pktype || (sshkey_type_plain(pktype) == KEY_ECDSA &&
|
||||
sshkey_ecdsa_nid_from_name(pkalg) != key->ecdsa_nid)) {
|
||||
error_f("key type mismatch for decoded key "
|
||||
"(received %s, expected %s)", sshkey_ssh_name(key), pkalg);
|
||||
goto done;
|
||||
}
|
||||
if ((r = sshkey_check_cert_sigtype(key,
|
||||
options.ca_sign_algorithms)) != 0) {
|
||||
logit_fr(r, "certificate signature algorithm %s",
|
||||
(key->cert == NULL || key->cert->signature_type == NULL) ?
|
||||
"(null)" : key->cert->signature_type);
|
||||
goto done;
|
||||
}
|
||||
if ((r = sshkey_check_rsa_length(key,
|
||||
options.required_rsa_size)) != 0) {
|
||||
logit_r(r, "refusing %s key", sshkey_type(key));
|
||||
@@ -135,7 +142,7 @@ userauth_hostbased(struct ssh *ssh, const char *method)
|
||||
(r = sshbuf_put_cstring(b, authctxt->service)) != 0 ||
|
||||
(r = sshbuf_put_cstring(b, method)) != 0 ||
|
||||
(r = sshbuf_put_string(b, pkalg, alen)) != 0 ||
|
||||
(r = sshbuf_put_string(b, pkblob, blen)) != 0 ||
|
||||
(r = sshbuf_put_stringb(b, keyblob)) != 0 ||
|
||||
(r = sshbuf_put_cstring(b, chost)) != 0 ||
|
||||
(r = sshbuf_put_cstring(b, cuser)) != 0)
|
||||
fatal_fr(r, "reconstruct packet");
|
||||
@@ -155,12 +162,12 @@ userauth_hostbased(struct ssh *ssh, const char *method)
|
||||
authenticated = 1;
|
||||
|
||||
auth2_record_key(authctxt, authenticated, key);
|
||||
sshbuf_free(b);
|
||||
done:
|
||||
debug2_f("authenticated %d", authenticated);
|
||||
sshkey_free(key);
|
||||
sshbuf_free(b);
|
||||
sshbuf_free(keyblob);
|
||||
free(pkalg);
|
||||
free(pkblob);
|
||||
free(cuser);
|
||||
free(chost);
|
||||
free(sig);
|
||||
|
||||
+62
-39
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: auth2-pubkey.c,v 1.128 2026/09/16 00:16:52 djm Exp $ */
|
||||
/* $OpenBSD: auth2-pubkey.c,v 1.129 2026/09/16 00:31:27 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
* Copyright (c) 2010 Damien Miller. All rights reserved.
|
||||
@@ -84,16 +84,41 @@ format_key(const struct sshkey *key)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that the hostkey in a publickey-hostbound-v00@openssh.com userauth
|
||||
* request matches the hostkey that was negotiatied during initial KEX.
|
||||
*/
|
||||
static void
|
||||
check_hostbound_hostkey(struct ssh *ssh)
|
||||
{
|
||||
int r;
|
||||
struct sshbuf *our_hostkey_blob, *their_hostkey_blob;
|
||||
|
||||
if (ssh->kex->initial_hostkey == NULL)
|
||||
fatal_f("internal error: initial hostkey not recorded");
|
||||
if ((our_hostkey_blob = sshbuf_new()) == NULL)
|
||||
fatal_f("sshbuf_new failed");
|
||||
if ((r = sshpkt_getb_froms(ssh, &their_hostkey_blob)) != 0)
|
||||
fatal_fr(r, "parse hostkey");
|
||||
if ((r = sshkey_putb(ssh->kex->initial_hostkey, our_hostkey_blob)) != 0)
|
||||
fatal_fr(r, "serialise hostkey");
|
||||
if ((r = sshbuf_equals(our_hostkey_blob, their_hostkey_blob)) != 0)
|
||||
fatal_f("packet contained wrong host key");
|
||||
sshbuf_free(our_hostkey_blob);
|
||||
sshbuf_free(their_hostkey_blob);
|
||||
}
|
||||
|
||||
static int
|
||||
userauth_pubkey(struct ssh *ssh, const char *method)
|
||||
{
|
||||
Authctxt *authctxt = ssh->authctxt;
|
||||
struct passwd *pw = authctxt->pw;
|
||||
struct sshbuf *b = NULL;
|
||||
struct sshbuf *keyblob = NULL, *b = NULL;
|
||||
struct sshkey *key = NULL, *hostkey = NULL;
|
||||
char *pkalg = NULL, *userstyle = NULL, *key_s = NULL, *ca_s = NULL;
|
||||
u_char *pkblob = NULL, *sig = NULL, have_sig;
|
||||
size_t blen, slen;
|
||||
char *keystring = NULL;
|
||||
u_char *sig = NULL, have_sig;
|
||||
size_t slen;
|
||||
int hostbound, r, pktype;
|
||||
int req_presence = 0, req_verify = 0, authenticated = 0;
|
||||
struct sshauthopt *authopts = NULL;
|
||||
@@ -103,35 +128,23 @@ userauth_pubkey(struct ssh *ssh, const char *method)
|
||||
|
||||
if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0 ||
|
||||
(r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 ||
|
||||
(r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0)
|
||||
(r = sshpkt_getb_froms(ssh, &keyblob)) != 0)
|
||||
fatal_fr(r, "parse %s packet", method);
|
||||
|
||||
/* hostbound auth includes the hostkey offered at initial KEX */
|
||||
if (hostbound) {
|
||||
if ((r = sshpkt_getb_froms(ssh, &b)) != 0 ||
|
||||
(r = sshkey_fromb(b, &hostkey)) != 0)
|
||||
fatal_fr(r, "parse %s hostkey", method);
|
||||
if (ssh->kex->initial_hostkey == NULL)
|
||||
fatal_f("internal error: initial hostkey not recorded");
|
||||
if (!sshkey_equal(hostkey, ssh->kex->initial_hostkey))
|
||||
fatal_f("%s packet contained wrong host key", method);
|
||||
sshbuf_free(b);
|
||||
b = NULL;
|
||||
}
|
||||
if (hostbound)
|
||||
check_hostbound_hostkey(ssh); /* fatals on error */
|
||||
|
||||
if (log_level_get() >= SYSLOG_LEVEL_DEBUG2) {
|
||||
char *keystring;
|
||||
struct sshbuf *pkbuf;
|
||||
|
||||
if ((pkbuf = sshbuf_from(pkblob, blen)) == NULL)
|
||||
fatal_f("sshbuf_from failed");
|
||||
if ((keystring = sshbuf_dtob64_string(pkbuf, 0)) == NULL)
|
||||
if ((b = sshbuf_fromb(keyblob)) == NULL)
|
||||
fatal_f("sshbuf_fromb failed");
|
||||
if ((keystring = sshbuf_dtob64_string(b, 0)) == NULL)
|
||||
fatal_f("sshbuf_dtob64 failed");
|
||||
debug2_f("%s user %s %s public key %s %s",
|
||||
authctxt->valid ? "valid" : "invalid", authctxt->user,
|
||||
have_sig ? "attempting" : "querying", pkalg, keystring);
|
||||
sshbuf_free(pkbuf);
|
||||
free(keystring);
|
||||
sshbuf_free(b);
|
||||
b = NULL;
|
||||
}
|
||||
|
||||
pktype = sshkey_type_from_name(pkalg);
|
||||
@@ -145,14 +158,30 @@ userauth_pubkey(struct ssh *ssh, const char *method)
|
||||
"PubkeyAcceptedAlgorithms", pkalg);
|
||||
goto done;
|
||||
}
|
||||
if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
|
||||
if ((b = sshbuf_fromb(keyblob)) == NULL)
|
||||
fatal_f("sshbuf_fromb failed");
|
||||
switch ((r = sshkey_fromb_allowlist(b, &key,
|
||||
options.pubkey_accepted_algos, options.ca_sign_algorithms))) {
|
||||
case 0:
|
||||
/* ok */
|
||||
break;
|
||||
case SSH_ERR_KEY_ALG_UNSUPPORTED:
|
||||
/* This shouldn't happen unless the client is being weird */
|
||||
logit_f("key algorithm differs from signature algorithm %s and "
|
||||
"is not in PubkeyAcceptedAlgorithms", pkalg);
|
||||
goto done;
|
||||
case SSH_ERR_SIGN_ALG_UNSUPPORTED:
|
||||
logit_fr(r, "certificate signature algorithm not in "
|
||||
"CASignatureAlgorithm");
|
||||
goto done;
|
||||
default:
|
||||
error_fr(r, "parse key");
|
||||
goto done;
|
||||
}
|
||||
if (key == NULL) {
|
||||
error_f("cannot decode key: %s", pkalg);
|
||||
goto done;
|
||||
}
|
||||
sshbuf_free(b);
|
||||
b = NULL;
|
||||
|
||||
/* USERAUTH_REQUEST signature type should match key's type */
|
||||
if (key->type != pktype || (sshkey_type_plain(pktype) == KEY_ECDSA &&
|
||||
sshkey_ecdsa_nid_from_name(pkalg) != key->ecdsa_nid)) {
|
||||
error_f("key type mismatch for decoded key "
|
||||
@@ -163,13 +192,6 @@ userauth_pubkey(struct ssh *ssh, const char *method)
|
||||
logit("refusing previously-used %s key", sshkey_type(key));
|
||||
goto done;
|
||||
}
|
||||
if ((r = sshkey_check_cert_sigtype(key,
|
||||
options.ca_sign_algorithms)) != 0) {
|
||||
logit_fr(r, "certificate signature algorithm %s",
|
||||
(key->cert == NULL || key->cert->signature_type == NULL) ?
|
||||
"(null)" : key->cert->signature_type);
|
||||
goto done;
|
||||
}
|
||||
if ((r = sshkey_check_rsa_length(key,
|
||||
options.required_rsa_size)) != 0) {
|
||||
logit_r(r, "refusing %s key", sshkey_type(key));
|
||||
@@ -210,7 +232,7 @@ userauth_pubkey(struct ssh *ssh, const char *method)
|
||||
(r = sshbuf_put_cstring(b, method)) != 0 ||
|
||||
(r = sshbuf_put_u8(b, have_sig)) != 0 ||
|
||||
(r = sshbuf_put_cstring(b, pkalg)) != 0 ||
|
||||
(r = sshbuf_put_string(b, pkblob, blen)) != 0)
|
||||
(r = sshbuf_put_stringb(b, keyblob)) != 0)
|
||||
fatal_fr(r, "reconstruct %s packet", method);
|
||||
if (hostbound &&
|
||||
(r = sshkey_puts(ssh->kex->initial_hostkey, b)) != 0)
|
||||
@@ -286,7 +308,7 @@ userauth_pubkey(struct ssh *ssh, const char *method)
|
||||
if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_PK_OK))
|
||||
!= 0 ||
|
||||
(r = sshpkt_put_cstring(ssh, pkalg)) != 0 ||
|
||||
(r = sshpkt_put_string(ssh, pkblob, blen)) != 0 ||
|
||||
(r = sshpkt_put_stringb(ssh, keyblob)) != 0 ||
|
||||
(r = sshpkt_send(ssh)) != 0 ||
|
||||
(r = ssh_packet_write_wait(ssh)) != 0)
|
||||
fatal_fr(r, "send packet");
|
||||
@@ -313,12 +335,13 @@ done:
|
||||
debug2_f("authenticated %d pkalg %s", authenticated, pkalg);
|
||||
|
||||
sshbuf_free(b);
|
||||
sshbuf_free(keyblob);
|
||||
sshauthopt_free(authopts);
|
||||
sshkey_free(key);
|
||||
sshkey_free(hostkey);
|
||||
free(keystring);
|
||||
free(userstyle);
|
||||
free(pkalg);
|
||||
free(pkblob);
|
||||
free(key_s);
|
||||
free(ca_s);
|
||||
free(sig);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: kexgen.c,v 1.14 2026/07/30 07:29:09 dtucker Exp $ */
|
||||
/* $OpenBSD: kexgen.c,v 1.15 2026/09/16 00:31:27 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2019 Markus Friedl. All rights reserved.
|
||||
*
|
||||
@@ -165,7 +165,8 @@ input_kex_gen_reply(int type, uint32_t seq, struct ssh *ssh)
|
||||
r = SSH_ERR_ALLOC_FAIL;
|
||||
goto out;
|
||||
}
|
||||
if ((r = sshkey_fromb(tmp, &server_host_key)) != 0)
|
||||
if ((r = sshkey_fromb_allowlist(tmp, &server_host_key,
|
||||
kex->hostkey_alg, NULL)) != 0)
|
||||
goto out;
|
||||
if ((r = kex_verify_host_key(ssh, server_host_key)) != 0)
|
||||
goto out;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: kexgexc.c,v 1.42 2026/03/03 09:57:25 dtucker Exp $ */
|
||||
/* $OpenBSD: kexgexc.c,v 1.43 2026/09/16 00:31:27 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000 Niels Provos. All rights reserved.
|
||||
* Copyright (c) 2001 Markus Friedl. All rights reserved.
|
||||
@@ -165,7 +165,8 @@ input_kex_dh_gex_reply(int type, uint32_t seq, struct ssh *ssh)
|
||||
r = SSH_ERR_ALLOC_FAIL;
|
||||
goto out;
|
||||
}
|
||||
if ((r = sshkey_fromb(tmp, &server_host_key)) != 0 ||
|
||||
if ((r = sshkey_fromb_allowlist(tmp, &server_host_key,
|
||||
kex->hostkey_alg, NULL)) != 0 ||
|
||||
(r = kex_verify_host_key(ssh, server_host_key)) != 0)
|
||||
goto out;
|
||||
/* DH parameter f, server public DH key, signed H */
|
||||
|
||||
+13
-7
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: sshconnect2.c,v 1.393 2026/08/08 07:25:55 djm Exp $ */
|
||||
/* $OpenBSD: sshconnect2.c,v 1.394 2026/09/16 00:31:27 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
* Copyright (c) 2008 Damien Miller. All rights reserved.
|
||||
@@ -683,27 +683,33 @@ input_userauth_pk_ok(int type, uint32_t seq, struct ssh *ssh)
|
||||
{
|
||||
Authctxt *authctxt = ssh->authctxt;
|
||||
struct sshkey *key = NULL;
|
||||
struct sshbuf *keyblob = NULL;
|
||||
Identity *id = NULL;
|
||||
int pktype, found = 0, sent = 0;
|
||||
size_t blen;
|
||||
char *pkalg = NULL, *fp = NULL, *ident = NULL;
|
||||
u_char *pkblob = NULL;
|
||||
int r;
|
||||
|
||||
if (authctxt == NULL)
|
||||
fatal("input_userauth_pk_ok: no authentication context");
|
||||
fatal_f("no authentication context");
|
||||
|
||||
if ((r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 ||
|
||||
(r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0 ||
|
||||
(r = sshpkt_getb_froms(ssh, &keyblob)) != 0 ||
|
||||
(r = sshpkt_get_end(ssh)) != 0)
|
||||
goto done;
|
||||
|
||||
if (match_pattern_list(pkalg, options.pubkey_accepted_algos, 0) != 1) {
|
||||
error_f("server replied to PK_OK with signature type %s not "
|
||||
"in PubkeyAcceptedAlgorithms", pkalg);
|
||||
r = SSH_ERR_SIGN_ALG_UNSUPPORTED;
|
||||
goto done;
|
||||
}
|
||||
if ((pktype = sshkey_type_from_name(pkalg)) == KEY_UNSPEC) {
|
||||
debug_f("server sent unknown pkalg %s", pkalg);
|
||||
r = SSH_ERR_INVALID_FORMAT;
|
||||
goto done;
|
||||
}
|
||||
if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
|
||||
/* inner key type should match signature type */
|
||||
if ((r = sshkey_fromb_allowlist(keyblob, &key, pkalg, NULL)) != 0) {
|
||||
debug_r(r, "no key from blob. pkalg %s", pkalg);
|
||||
goto done;
|
||||
}
|
||||
@@ -740,10 +746,10 @@ input_userauth_pk_ok(int type, uint32_t seq, struct ssh *ssh)
|
||||
r = 0;
|
||||
done:
|
||||
sshkey_free(key);
|
||||
sshbuf_free(keyblob);
|
||||
free(ident);
|
||||
free(fp);
|
||||
free(pkalg);
|
||||
free(pkblob);
|
||||
|
||||
/* try another method if we did not send a packet */
|
||||
if (r == 0 && sent == 0)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: ssherr.c,v 1.12 2026/06/14 03:59:34 djm Exp $ */
|
||||
/* $OpenBSD: ssherr.c,v 1.13 2026/09/16 00:31:27 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2011 Damien Miller
|
||||
*
|
||||
@@ -144,6 +144,8 @@ ssh_err(int n)
|
||||
return "Invalid key length";
|
||||
case SSH_ERR_NUMBER_TOO_LARGE:
|
||||
return "number is too large";
|
||||
case SSH_ERR_KEY_ALG_UNSUPPORTED:
|
||||
return "key algorithm not supported";
|
||||
case SSH_ERR_SIGN_ALG_UNSUPPORTED:
|
||||
return "signature algorithm not supported";
|
||||
case SSH_ERR_FEATURE_UNSUPPORTED:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: ssherr.h,v 1.10 2026/06/14 03:59:34 djm Exp $ */
|
||||
/* $OpenBSD: ssherr.h,v 1.11 2026/09/16 00:31:27 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2011 Damien Miller
|
||||
*
|
||||
@@ -84,6 +84,7 @@
|
||||
#define SSH_ERR_DEVICE_NOT_FOUND -60
|
||||
#define SSH_ERR_CRYPTO_ERROR -61
|
||||
#define SSH_ERR_INTERNAL_CRYPTO_ERROR -62
|
||||
#define SSH_ERR_KEY_ALG_UNSUPPORTED -63
|
||||
|
||||
/* Translate a numeric error code to a human-readable error string */
|
||||
const char *ssh_err(int n);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: sshkey.c,v 1.163 2026/06/29 01:58:29 djm Exp $ */
|
||||
/* $OpenBSD: sshkey.c,v 1.164 2026/09/16 00:31:27 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
|
||||
* Copyright (c) 2008 Alexander von Gernler. All rights reserved.
|
||||
@@ -83,7 +83,8 @@
|
||||
#define SSHKEY_SHIELD_PREKEY_HASH SSH_DIGEST_SHA512
|
||||
|
||||
static int sshkey_from_blob_internal(struct sshbuf *buf,
|
||||
struct sshkey **keyp, int allow_cert);
|
||||
struct sshkey **keyp, int allow_cert,
|
||||
const char *alg_allowlist, const char *ca_sigalg_allowlist);
|
||||
|
||||
/* Supported key types */
|
||||
extern const struct sshkey_impl sshkey_ed25519_impl;
|
||||
@@ -1849,11 +1850,13 @@ sshkey_unshield_private(struct sshkey *k)
|
||||
}
|
||||
|
||||
static int
|
||||
cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf)
|
||||
cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf,
|
||||
const char *ca_sigalg_allowlist)
|
||||
{
|
||||
struct sshbuf *principals = NULL, *crit = NULL;
|
||||
struct sshbuf *exts = NULL, *ca = NULL;
|
||||
u_char *sig = NULL;
|
||||
char *sigtype = NULL;
|
||||
size_t signed_len = 0, slen = 0, kidlen = 0;
|
||||
int ret = SSH_ERR_INTERNAL_ERROR;
|
||||
|
||||
@@ -1885,6 +1888,28 @@ cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Is this a signature we're prepared to accept? */
|
||||
if ((ret = sshkey_get_sigtype(sig, slen, &sigtype)) != 0) {
|
||||
ret = SSH_ERR_INVALID_FORMAT;
|
||||
goto out;
|
||||
}
|
||||
if (ca_sigalg_allowlist != NULL &&
|
||||
match_pattern_list(sigtype, ca_sigalg_allowlist, 0) != 1) {
|
||||
ret = SSH_ERR_SIGN_ALG_UNSUPPORTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Parse CA key and check whether we might accept it */
|
||||
if (sshkey_from_blob_internal(ca, &key->cert->signature_key, 0,
|
||||
ca_sigalg_allowlist, NULL) != 0) {
|
||||
ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
|
||||
goto out;
|
||||
}
|
||||
if (!sshkey_type_is_valid_ca(key->cert->signature_key->type)) {
|
||||
ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (key->cert->type != SSH2_CERT_TYPE_USER &&
|
||||
key->cert->type != SSH2_CERT_TYPE_HOST) {
|
||||
ret = SSH_ERR_KEY_CERT_UNKNOWN_TYPE;
|
||||
@@ -1947,30 +1972,22 @@ cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf)
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse CA key and check signature */
|
||||
if (sshkey_from_blob_internal(ca, &key->cert->signature_key, 0) != 0) {
|
||||
ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
|
||||
goto out;
|
||||
}
|
||||
if (!sshkey_type_is_valid_ca(key->cert->signature_key->type)) {
|
||||
ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
|
||||
goto out;
|
||||
}
|
||||
/* Finally, validate signature */
|
||||
if ((ret = sshkey_verify(key->cert->signature_key, sig, slen,
|
||||
sshbuf_ptr(key->cert->certblob), signed_len, NULL, 0, NULL)) != 0)
|
||||
goto out;
|
||||
if ((ret = sshkey_get_sigtype(sig, slen,
|
||||
&key->cert->signature_type)) != 0)
|
||||
goto out;
|
||||
|
||||
/* Success */
|
||||
ret = 0;
|
||||
key->cert->signature_type = sigtype;
|
||||
sigtype = NULL;
|
||||
out:
|
||||
sshbuf_free(ca);
|
||||
sshbuf_free(crit);
|
||||
sshbuf_free(exts);
|
||||
sshbuf_free(principals);
|
||||
free(sig);
|
||||
free(sigtype);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1985,7 +2002,7 @@ sshkey_deserialize_sk(struct sshbuf *b, struct sshkey *key)
|
||||
|
||||
static int
|
||||
sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
|
||||
int allow_cert)
|
||||
int allow_cert, const char *alg_allowlist, const char *ca_sigalg_allowlist)
|
||||
{
|
||||
int type, ret = SSH_ERR_INTERNAL_ERROR;
|
||||
char *ktype = NULL;
|
||||
@@ -2012,6 +2029,13 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
|
||||
ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (alg_allowlist != NULL &&
|
||||
!sshkey_match_keyname_to_sigalgs(ktype, alg_allowlist)) {
|
||||
ret = SSH_ERR_KEY_ALG_UNSUPPORTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((impl = sshkey_impl_from_type(type)) == NULL) {
|
||||
ret = SSH_ERR_KEY_TYPE_UNKNOWN;
|
||||
goto out;
|
||||
@@ -2031,7 +2055,8 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
|
||||
goto out;
|
||||
|
||||
/* Parse certificate potion */
|
||||
if (sshkey_is_cert(key) && (ret = cert_parse(b, key, copy)) != 0)
|
||||
if (sshkey_is_cert(key) &&
|
||||
(ret = cert_parse(b, key, copy, ca_sigalg_allowlist)) != 0)
|
||||
goto out;
|
||||
|
||||
if (key != NULL && sshbuf_len(b) != 0) {
|
||||
@@ -2058,7 +2083,7 @@ sshkey_from_blob(const u_char *blob, size_t blen, struct sshkey **keyp)
|
||||
|
||||
if ((b = sshbuf_from(blob, blen)) == NULL)
|
||||
return SSH_ERR_ALLOC_FAIL;
|
||||
r = sshkey_from_blob_internal(b, keyp, 1);
|
||||
r = sshkey_from_blob_internal(b, keyp, 1, NULL, NULL);
|
||||
sshbuf_free(b);
|
||||
return r;
|
||||
}
|
||||
@@ -2066,7 +2091,15 @@ sshkey_from_blob(const u_char *blob, size_t blen, struct sshkey **keyp)
|
||||
int
|
||||
sshkey_fromb(struct sshbuf *b, struct sshkey **keyp)
|
||||
{
|
||||
return sshkey_from_blob_internal(b, keyp, 1);
|
||||
return sshkey_from_blob_internal(b, keyp, 1, NULL, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
sshkey_fromb_allowlist(struct sshbuf *b, struct sshkey **keyp,
|
||||
const char *alg_allowlist, const char *ca_sigalg_allowlist)
|
||||
{
|
||||
return sshkey_from_blob_internal(b, keyp, 1,
|
||||
alg_allowlist, ca_sigalg_allowlist);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -2077,7 +2110,7 @@ sshkey_froms(struct sshbuf *buf, struct sshkey **keyp)
|
||||
|
||||
if ((r = sshbuf_froms(buf, &b)) != 0)
|
||||
return r;
|
||||
r = sshkey_from_blob_internal(b, keyp, 1);
|
||||
r = sshkey_from_blob_internal(b, keyp, 1, NULL, NULL);
|
||||
sshbuf_free(b);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: sshkey.h,v 1.74 2026/06/14 03:59:34 djm Exp $ */
|
||||
/* $OpenBSD: sshkey.h,v 1.75 2026/09/16 00:31:27 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
|
||||
@@ -253,6 +253,8 @@ char *sshkey_alg_list(int, int, int, char);
|
||||
|
||||
int sshkey_from_blob(const u_char *, size_t, struct sshkey **);
|
||||
int sshkey_fromb(struct sshbuf *, struct sshkey **);
|
||||
int sshkey_fromb_allowlist(struct sshbuf *, struct sshkey **,
|
||||
const char *, const char *);
|
||||
int sshkey_froms(struct sshbuf *, struct sshkey **);
|
||||
int sshkey_to_blob(const struct sshkey *, u_char **, size_t *);
|
||||
int sshkey_to_base64(const struct sshkey *, char **);
|
||||
|
||||
Reference in new issue
Block a user