sync with OpenBSD -current

This commit is contained in:
purplerain 2023-12-18 23:55:01 +00:00
parent da785accdf
commit 659ea2942e
Signed by: purplerain
GPG key ID: F42C07F07E2E35B7
39 changed files with 1318 additions and 384 deletions

View file

@ -137,6 +137,51 @@ than as a named global or channel request to allow pings with very
short packet lengths, which would not be possible with other
approaches.
1.9 transport: strict key exchange extension
OpenSSH supports a number of transport-layer hardening measures under
a "strict KEX" feature. This feature is signalled similarly to the
RFC8308 ext-info feature: by including a additional algorithm in the
initiial SSH2_MSG_KEXINIT kex_algorithms field. The client may append
"kex-strict-c-v00@openssh.com" to its kex_algorithms and the server
may append "kex-strict-s-v00@openssh.com". These pseudo-algorithms
are only valid in the initial SSH2_MSG_KEXINIT and MUST be ignored
if they are present in subsequent SSH2_MSG_KEXINIT packets.
When an endpoint that supports this extension observes this algorithm
name in a peer's KEXINIT packet, it MUST make the following changes to
the the protocol:
a) During initial KEX, terminate the connection if any unexpected or
out-of-sequence packet is received. This includes terminating the
connection if the first packet received is not SSH2_MSG_KEXINIT.
Unexpected packets for the purpose of strict KEX include messages
that are otherwise valid at any time during the connection such as
SSH2_MSG_DEBUG and SSH2_MSG_IGNORE.
b) After sending or receiving a SSH2_MSG_NEWKEYS message, reset the
packet sequence number to zero. This behaviour persists for the
duration of the connection (i.e. not just the first
SSH2_MSG_NEWKEYS).
1.10 transport: SSH2_MSG_EXT_INFO during user authentication
This protocol extension allows the SSH2_MSG_EXT_INFO to be sent
during user authentication. RFC8308 does allow a second
SSH2_MSG_EXT_INFO notification, but it may only be sent at the end
of user authentication and this is too late to signal per-user
server signature algorithms.
Support for receiving the SSH2_MSG_EXT_INFO message during user
authentication is signalled by the client including a
"ext-info-in-auth@openssh.com" key via its initial SSH2_MSG_EXT_INFO
set after the SSH2_MSG_NEWKEYS message.
A server that supports this extension MAY send a second
SSH2_MSG_EXT_INFO message any time after the client's first
SSH2_MSG_USERAUTH_REQUEST, regardless of whether it succeed or fails.
The client SHOULD be prepared to update the server-sig-algs that
it received during an earlier SSH2_MSG_EXT_INFO with the later one.
2. Connection protocol changes
2.1. connection: Channel write close extension "eow@openssh.com"
@ -745,4 +790,4 @@ master instance and later clients.
OpenSSH extends the usual agent protocol. These changes are documented
in the PROTOCOL.agent file.
$OpenBSD: PROTOCOL,v 1.49 2023/08/28 03:28:43 djm Exp $
$OpenBSD: PROTOCOL,v 1.51 2023/12/18 14:45:49 djm Exp $

View file

@ -81,4 +81,35 @@ the constraint is:
This option is only valid for XMSS keys.
$OpenBSD: PROTOCOL.agent,v 1.20 2023/10/03 23:56:10 djm Exp $
3. associated-certs-v00@openssh.com key constraint extension
The key constraint extension allows certificates to be associated
with private keys as they are loaded from a PKCS#11 token.
byte SSH_AGENT_CONSTRAIN_EXTENSION (0xff)
string associated-certs-v00@openssh.com
bool certs_only
string certsblob
Where "certsblob" constists of one or more certificates encoded as public
key blobs:
string[] certificates
This extension is only valid for SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED
requests. When an agent receives this extension, it will attempt to match
each certificate in the request with a corresponding private key loaded
from the requested PKCS#11 token. When a matching key is found, the
agent will graft the certificate contents to the token-hosted private key
and store the result for subsequent use by regular agent operations.
If the "certs_only" flag is set, then this extension will cause ONLY
the resultant certificates to be loaded to the agent. The default
behaviour is to load the PKCS#11-hosted private key as well as the
resultant certificate.
A SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED will return SSH_AGENT_SUCCESS
if any key (plain private or certificate) was successfully loaded, or
SSH_AGENT_FAILURE if no key was loaded.
$OpenBSD: PROTOCOL.agent,v 1.21 2023/12/18 14:46:56 djm Exp $

View file

@ -1,4 +1,4 @@
/* $OpenBSD: auth2.c,v 1.167 2023/08/28 09:48:11 djm Exp $ */
/* $OpenBSD: auth2.c,v 1.168 2023/12/18 14:45:49 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
@ -55,6 +55,7 @@
#include "monitor_wrap.h"
#include "ssherr.h"
#include "digest.h"
#include "kex.h"
/* import */
extern ServerOptions options;
@ -162,6 +163,8 @@ do_authentication2(struct ssh *ssh)
Authctxt *authctxt = ssh->authctxt;
ssh_dispatch_init(ssh, &dispatch_protocol_error);
if (ssh->kex->ext_info_c)
ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_input_ext_info);
ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_REQUEST, &input_service_request);
ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt->success);
ssh->authctxt = NULL;
@ -201,6 +204,7 @@ input_service_request(int type, u_int32_t seq, struct ssh *ssh)
debug("bad service request %s", service);
ssh_packet_disconnect(ssh, "bad service request %s", service);
}
ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &dispatch_protocol_error);
r = 0;
out:
free(service);
@ -296,6 +300,8 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
if (use_privsep)
mm_inform_authserv(service, style);
userauth_banner(ssh);
if ((r = kex_server_update_ext_info(ssh)) != 0)
fatal_fr(r, "kex_server_update_ext_info failed");
if (auth2_setup_methods_lists(authctxt) != 0)
ssh_packet_disconnect(ssh,
"no authentication methods enabled");

View file

@ -1,4 +1,4 @@
/* $OpenBSD: authfd.c,v 1.133 2023/03/09 21:06:24 jcs Exp $ */
/* $OpenBSD: authfd.c,v 1.134 2023/12/18 14:46:56 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -503,9 +503,10 @@ encode_dest_constraint(struct sshbuf *m, const struct dest_constraint *dc)
}
static int
encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign,
const char *provider, struct dest_constraint **dest_constraints,
size_t ndest_constraints)
encode_constraints(struct sshbuf *m, u_int life, u_int confirm,
u_int maxsign, const char *provider,
struct dest_constraint **dest_constraints, size_t ndest_constraints,
int cert_only, struct sshkey **certs, size_t ncerts)
{
int r;
struct sshbuf *b = NULL;
@ -549,6 +550,27 @@ encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign,
"restrict-destination-v00@openssh.com")) != 0 ||
(r = sshbuf_put_stringb(m, b)) != 0)
goto out;
sshbuf_free(b);
b = NULL;
}
if (ncerts != 0) {
if ((b = sshbuf_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
for (i = 0; i < ncerts; i++) {
if ((r = sshkey_puts(certs[i], b)) != 0)
goto out;
}
if ((r = sshbuf_put_u8(m,
SSH_AGENT_CONSTRAIN_EXTENSION)) != 0 ||
(r = sshbuf_put_cstring(m,
"associated-certs-v00@openssh.com")) != 0 ||
(r = sshbuf_put_u8(m, cert_only != 0)) != 0 ||
(r = sshbuf_put_stringb(m, b)) != 0)
goto out;
sshbuf_free(b);
b = NULL;
}
r = 0;
out:
@ -606,7 +628,7 @@ ssh_add_identity_constrained(int sock, struct sshkey *key,
}
if (constrained &&
(r = encode_constraints(msg, life, confirm, maxsign,
provider, dest_constraints, ndest_constraints)) != 0)
provider, dest_constraints, ndest_constraints, 0, NULL, 0)) != 0)
goto out;
if ((r = ssh_request_reply_decode(sock, msg)) != 0)
goto out;
@ -661,10 +683,11 @@ ssh_remove_identity(int sock, const struct sshkey *key)
int
ssh_update_card(int sock, int add, const char *reader_id, const char *pin,
u_int life, u_int confirm,
struct dest_constraint **dest_constraints, size_t ndest_constraints)
struct dest_constraint **dest_constraints, size_t ndest_constraints,
int cert_only, struct sshkey **certs, size_t ncerts)
{
struct sshbuf *msg;
int r, constrained = (life || confirm || dest_constraints);
int r, constrained = (life || confirm || dest_constraints || certs);
u_char type;
if (add) {
@ -682,7 +705,8 @@ ssh_update_card(int sock, int add, const char *reader_id, const char *pin,
goto out;
if (constrained &&
(r = encode_constraints(msg, life, confirm, 0, NULL,
dest_constraints, ndest_constraints)) != 0)
dest_constraints, ndest_constraints,
cert_only, certs, ncerts)) != 0)
goto out;
if ((r = ssh_request_reply_decode(sock, msg)) != 0)
goto out;

View file

@ -1,4 +1,4 @@
/* $OpenBSD: authfd.h,v 1.51 2021/12/19 22:10:24 djm Exp $ */
/* $OpenBSD: authfd.h,v 1.52 2023/12/18 14:46:56 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -56,7 +56,8 @@ int ssh_remove_identity(int sock, const struct sshkey *key);
int ssh_update_card(int sock, int add, const char *reader_id,
const char *pin, u_int life, u_int confirm,
struct dest_constraint **dest_constraints,
size_t ndest_constraints);
size_t ndest_constraints,
int cert_only, struct sshkey **certs, size_t ncerts);
int ssh_remove_all_identities(int sock, int version);
int ssh_agent_sign(int sock, const struct sshkey *key,

View file

@ -1,4 +1,4 @@
/* $OpenBSD: channels.c,v 1.434 2023/11/15 22:51:49 djm Exp $ */
/* $OpenBSD: channels.c,v 1.435 2023/12/18 14:47:20 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -3365,11 +3365,20 @@ channel_input_data(int type, u_int32_t seq, struct ssh *ssh)
return 0;
}
if (win_len > c->local_window) {
logit("channel %d: rcvd too much data %zu, win %u",
c->self, win_len, c->local_window);
return 0;
c->local_window_exceeded += win_len - c->local_window;
logit("channel %d: rcvd too much data %zu, win %u/%u "
"(excess %u)", c->self, win_len, c->local_window,
c->local_window_max, c->local_window_exceeded);
c->local_window = 0;
/* Allow 10% grace before bringing the hammer down */
if (c->local_window_exceeded > (c->local_window_max / 10)) {
ssh_packet_disconnect(ssh, "channel %d: peer ignored "
"channel window", c->self);
}
} else {
c->local_window -= win_len;
c->local_window_exceeded = 0;
}
c->local_window -= win_len;
if (c->datagram) {
if ((r = sshbuf_put_string(c->output, data, data_len)) != 0)

View file

@ -1,4 +1,4 @@
/* $OpenBSD: channels.h,v 1.153 2023/11/15 22:51:49 djm Exp $ */
/* $OpenBSD: channels.h,v 1.154 2023/12/18 14:47:20 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -167,6 +167,7 @@ struct Channel {
u_int remote_window;
u_int remote_maxpacket;
u_int local_window;
u_int local_window_exceeded;
u_int local_window_max;
u_int local_consumed;
u_int local_maxpacket;

View file

@ -1,4 +1,4 @@
/* $OpenBSD: kex.c,v 1.182 2023/10/11 04:46:29 djm Exp $ */
/* $OpenBSD: kex.c,v 1.184 2023/12/18 14:45:49 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
*
@ -60,7 +60,7 @@
#include "xmalloc.h"
/* prototype */
static int kex_choose_conf(struct ssh *);
static int kex_choose_conf(struct ssh *, uint32_t seq);
static int kex_input_newkeys(int, u_int32_t, struct ssh *);
static const char * const proposal_names[PROPOSAL_MAX] = {
@ -162,6 +162,18 @@ kex_names_valid(const char *names)
return 1;
}
/* returns non-zero if proposal contains any algorithm from algs */
static int
has_any_alg(const char *proposal, const char *algs)
{
char *cp;
if ((cp = match_list(proposal, algs, NULL)) == NULL)
return 0;
free(cp);
return 1;
}
/*
* Concatenate algorithm names, avoiding duplicates in the process.
* Caller must free returned string.
@ -169,7 +181,7 @@ kex_names_valid(const char *names)
char *
kex_names_cat(const char *a, const char *b)
{
char *ret = NULL, *tmp = NULL, *cp, *p, *m;
char *ret = NULL, *tmp = NULL, *cp, *p;
size_t len;
if (a == NULL || *a == '\0')
@ -186,10 +198,8 @@ kex_names_cat(const char *a, const char *b)
}
strlcpy(ret, a, len);
for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) {
if ((m = match_list(ret, p, NULL)) != NULL) {
free(m);
if (has_any_alg(ret, p))
continue; /* Algorithm already present */
}
if (strlcat(ret, ",", len) >= len ||
strlcat(ret, p, len) >= len) {
free(tmp);
@ -319,15 +329,23 @@ kex_proposal_populate_entries(struct ssh *ssh, char *prop[PROPOSAL_MAX],
const char *defpropclient[PROPOSAL_MAX] = { KEX_CLIENT };
const char **defprop = ssh->kex->server ? defpropserver : defpropclient;
u_int i;
char *cp;
if (prop == NULL)
fatal_f("proposal missing");
/* Append EXT_INFO signalling to KexAlgorithms */
if (kexalgos == NULL)
kexalgos = defprop[PROPOSAL_KEX_ALGS];
if ((cp = kex_names_cat(kexalgos, ssh->kex->server ?
"ext-info-s,kex-strict-s-v00@openssh.com" :
"ext-info-c,kex-strict-c-v00@openssh.com")) == NULL)
fatal_f("kex_names_cat");
for (i = 0; i < PROPOSAL_MAX; i++) {
switch(i) {
case PROPOSAL_KEX_ALGS:
prop[i] = compat_kex_proposal(ssh,
kexalgos ? kexalgos : defprop[i]);
prop[i] = compat_kex_proposal(ssh, cp);
break;
case PROPOSAL_ENC_ALGS_CTOS:
case PROPOSAL_ENC_ALGS_STOC:
@ -348,6 +366,7 @@ kex_proposal_populate_entries(struct ssh *ssh, char *prop[PROPOSAL_MAX],
prop[i] = xstrdup(defprop[i]);
}
}
free(cp);
}
void
@ -451,7 +470,12 @@ kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh)
{
int r;
error("kex protocol error: type %d seq %u", type, seq);
/* If in strict mode, any unexpected message is an error */
if ((ssh->kex->flags & KEX_INITIAL) && ssh->kex->kex_strict) {
ssh_packet_disconnect(ssh, "strict KEX violation: "
"unexpected packet type %u (seqnr %u)", type, seq);
}
error_f("type %u seq %u", type, seq);
if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||
(r = sshpkt_put_u32(ssh, seq)) != 0 ||
(r = sshpkt_send(ssh)) != 0)
@ -466,36 +490,138 @@ kex_reset_dispatch(struct ssh *ssh)
SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
}
void
kex_set_server_sig_algs(struct ssh *ssh, const char *allowed_algs)
{
char *alg, *oalgs, *algs, *sigalgs;
const char *sigalg;
/*
* NB. allowed algorithms may contain certificate algorithms that
* map to a specific plain signature type, e.g.
* rsa-sha2-512-cert-v01@openssh.com => rsa-sha2-512
* We need to be careful here to match these, retain the mapping
* and only add each signature algorithm once.
*/
if ((sigalgs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
fatal_f("sshkey_alg_list failed");
oalgs = algs = xstrdup(allowed_algs);
free(ssh->kex->server_sig_algs);
ssh->kex->server_sig_algs = NULL;
for ((alg = strsep(&algs, ",")); alg != NULL && *alg != '\0';
(alg = strsep(&algs, ","))) {
if ((sigalg = sshkey_sigalg_by_name(alg)) == NULL)
continue;
if (!has_any_alg(sigalg, sigalgs))
continue;
/* Don't add an algorithm twice. */
if (ssh->kex->server_sig_algs != NULL &&
has_any_alg(sigalg, ssh->kex->server_sig_algs))
continue;
xextendf(&ssh->kex->server_sig_algs, ",", "%s", sigalg);
}
free(oalgs);
free(sigalgs);
if (ssh->kex->server_sig_algs == NULL)
ssh->kex->server_sig_algs = xstrdup("");
}
static int
kex_send_ext_info(struct ssh *ssh)
kex_compose_ext_info_server(struct ssh *ssh, struct sshbuf *m)
{
int r;
char *algs;
debug("Sending SSH2_MSG_EXT_INFO");
if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
if (ssh->kex->server_sig_algs == NULL &&
(ssh->kex->server_sig_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, 3)) != 0 ||
(r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
(r = sshpkt_put_cstring(ssh, algs)) != 0 ||
(r = sshpkt_put_cstring(ssh,
if ((r = sshbuf_put_u32(m, 3)) != 0 ||
(r = sshbuf_put_cstring(m, "server-sig-algs")) != 0 ||
(r = sshbuf_put_cstring(m, ssh->kex->server_sig_algs)) != 0 ||
(r = sshbuf_put_cstring(m,
"publickey-hostbound@openssh.com")) != 0 ||
(r = sshpkt_put_cstring(ssh, "0")) != 0 ||
(r = sshpkt_put_cstring(ssh, "ping@openssh.com")) != 0 ||
(r = sshpkt_put_cstring(ssh, "0")) != 0 ||
(r = sshpkt_send(ssh)) != 0) {
(r = sshbuf_put_cstring(m, "0")) != 0 ||
(r = sshbuf_put_cstring(m, "ping@openssh.com")) != 0 ||
(r = sshbuf_put_cstring(m, "0")) != 0) {
error_fr(r, "compose");
return r;
}
return 0;
}
static int
kex_compose_ext_info_client(struct ssh *ssh, struct sshbuf *m)
{
int r;
if ((r = sshbuf_put_u32(m, 1)) != 0 ||
(r = sshbuf_put_cstring(m, "ext-info-in-auth@openssh.com")) != 0 ||
(r = sshbuf_put_cstring(m, "0")) != 0) {
error_fr(r, "compose");
goto out;
}
/* success */
r = 0;
out:
free(algs);
return r;
}
static int
kex_maybe_send_ext_info(struct ssh *ssh)
{
int r;
struct sshbuf *m = NULL;
if ((ssh->kex->flags & KEX_INITIAL) == 0)
return 0;
if (!ssh->kex->ext_info_c && !ssh->kex->ext_info_s)
return 0;
/* Compose EXT_INFO packet. */
if ((m = sshbuf_new()) == NULL)
fatal_f("sshbuf_new failed");
if (ssh->kex->ext_info_c &&
(r = kex_compose_ext_info_server(ssh, m)) != 0)
goto fail;
if (ssh->kex->ext_info_s &&
(r = kex_compose_ext_info_client(ssh, m)) != 0)
goto fail;
/* Send the actual KEX_INFO packet */
debug("Sending SSH2_MSG_EXT_INFO");
if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
(r = sshpkt_putb(ssh, m)) != 0 ||
(r = sshpkt_send(ssh)) != 0) {
error_f("send EXT_INFO");
goto fail;
}
r = 0;
fail:
sshbuf_free(m);
return r;
}
int
kex_server_update_ext_info(struct ssh *ssh)
{
int r;
if ((ssh->kex->flags & KEX_HAS_EXT_INFO_IN_AUTH) == 0)
return 0;
debug_f("Sending SSH2_MSG_EXT_INFO");
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 ||
(r = sshpkt_put_cstring(ssh, ssh->kex->server_sig_algs)) != 0 ||
(r = sshpkt_send(ssh)) != 0) {
error_f("send EXT_INFO");
return r;
}
return 0;
}
int
kex_send_newkeys(struct ssh *ssh)
{
@ -507,9 +633,8 @@ kex_send_newkeys(struct ssh *ssh)
return r;
debug("SSH2_MSG_NEWKEYS sent");
ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys);
if (ssh->kex->ext_info_c && (ssh->kex->flags & KEX_INITIAL) != 0)
if ((r = kex_send_ext_info(ssh)) != 0)
return r;
if ((r = kex_maybe_send_ext_info(ssh)) != 0)
return r;
debug("expecting SSH2_MSG_NEWKEYS");
return 0;
}
@ -531,10 +656,61 @@ kex_ext_info_check_ver(struct kex *kex, const char *name,
return 0;
}
static int
kex_ext_info_client_parse(struct ssh *ssh, const char *name,
const u_char *value, size_t vlen)
{
int r;
/* NB. some messages are only accepted in the initial EXT_INFO */
if (strcmp(name, "server-sig-algs") == 0) {
/* Ensure no \0 lurking in value */
if (memchr(value, '\0', vlen) != NULL) {
error_f("nul byte in %s", name);
return SSH_ERR_INVALID_FORMAT;
}
debug_f("%s=<%s>", name, value);
free(ssh->kex->server_sig_algs);
ssh->kex->server_sig_algs = xstrdup((const char *)value);
} else if (ssh->kex->ext_info_received == 1 &&
strcmp(name, "publickey-hostbound@openssh.com") == 0) {
if ((r = kex_ext_info_check_ver(ssh->kex, name, value, vlen,
"0", KEX_HAS_PUBKEY_HOSTBOUND)) != 0) {
return r;
}
} else if (ssh->kex->ext_info_received == 1 &&
strcmp(name, "ping@openssh.com") == 0) {
if ((r = kex_ext_info_check_ver(ssh->kex, name, value, vlen,
"0", KEX_HAS_PING)) != 0) {
return r;
}
} else
debug_f("%s (unrecognised)", name);
return 0;
}
static int
kex_ext_info_server_parse(struct ssh *ssh, const char *name,
const u_char *value, size_t vlen)
{
int r;
if (strcmp(name, "ext-info-in-auth@openssh.com") == 0) {
if ((r = kex_ext_info_check_ver(ssh->kex, name, value, vlen,
"0", KEX_HAS_EXT_INFO_IN_AUTH)) != 0) {
return r;
}
} else
debug_f("%s (unrecognised)", name);
return 0;
}
int
kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
{
struct kex *kex = ssh->kex;
const int max_ext_info = kex->server ? 1 : 2;
u_int32_t i, ninfo;
char *name;
u_char *val;
@ -542,13 +718,17 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
int r;
debug("SSH2_MSG_EXT_INFO received");
if (++kex->ext_info_received > max_ext_info) {
error("too many SSH2_MSG_EXT_INFO messages sent by peer");
return dispatch_protocol_error(type, seq, ssh);
}
ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error);
if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0)
return r;
if (ninfo >= 1024) {
error("SSH2_MSG_EXT_INFO with too many entries, expected "
"<=1024, received %u", ninfo);
return SSH_ERR_INVALID_FORMAT;
return dispatch_protocol_error(type, seq, ssh);
}
for (i = 0; i < ninfo; i++) {
if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0)
@ -557,34 +737,16 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
free(name);
return r;
}
if (strcmp(name, "server-sig-algs") == 0) {
/* Ensure no \0 lurking in value */
if (memchr(val, '\0', vlen) != NULL) {
error_f("nul byte in %s", name);
free(name);
free(val);
return SSH_ERR_INVALID_FORMAT;
}
debug_f("%s=<%s>", name, val);
kex->server_sig_algs = val;
val = NULL;
} else if (strcmp(name,
"publickey-hostbound@openssh.com") == 0) {
if ((r = kex_ext_info_check_ver(kex, name, val, vlen,
"0", KEX_HAS_PUBKEY_HOSTBOUND)) != 0) {
free(name);
free(val);
debug3_f("extension %s", name);
if (kex->server) {
if ((r = kex_ext_info_server_parse(ssh, name,
val, vlen)) != 0)
return r;
}
} else if (strcmp(name, "ping@openssh.com") == 0) {
if ((r = kex_ext_info_check_ver(kex, name, val, vlen,
"0", KEX_HAS_PING)) != 0) {
free(name);
free(val);
} else {
if ((r = kex_ext_info_client_parse(ssh, name,
val, vlen)) != 0)
return r;
}
} else
debug_f("%s (unrecognised)", name);
}
free(name);
free(val);
}
@ -598,6 +760,8 @@ kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh)
int r;
debug("SSH2_MSG_NEWKEYS received");
if (kex->ext_info_c && (kex->flags & KEX_INITIAL) != 0)
ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_input_ext_info);
ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error);
ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
if ((r = sshpkt_get_end(ssh)) != 0)
@ -666,7 +830,7 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
error_f("no kex");
return SSH_ERR_INTERNAL_ERROR;
}
ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL);
ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_protocol_error);
ptr = sshpkt_ptr(ssh, &dlen);
if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
return r;
@ -702,7 +866,7 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)
if (!(kex->flags & KEX_INIT_SENT))
if ((r = kex_send_kexinit(ssh)) != 0)
return r;
if ((r = kex_choose_conf(ssh)) != 0)
if ((r = kex_choose_conf(ssh, seq)) != 0)
return r;
if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
@ -964,20 +1128,14 @@ proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
return (1);
}
/* returns non-zero if proposal contains any algorithm from algs */
static int
has_any_alg(const char *proposal, const char *algs)
kexalgs_contains(char **peer, const char *ext)
{
char *cp;
if ((cp = match_list(proposal, algs, NULL)) == NULL)
return 0;
free(cp);
return 1;
return has_any_alg(peer[PROPOSAL_KEX_ALGS], ext);
}
static int
kex_choose_conf(struct ssh *ssh)
kex_choose_conf(struct ssh *ssh, uint32_t seq)
{
struct kex *kex = ssh->kex;
struct newkeys *newkeys;
@ -1002,13 +1160,24 @@ kex_choose_conf(struct ssh *ssh)
sprop=peer;
}
/* Check whether client supports ext_info_c */
if (kex->server && (kex->flags & KEX_INITIAL)) {
char *ext;
ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);
kex->ext_info_c = (ext != NULL);
free(ext);
/* Check whether peer supports ext_info/kex_strict */
if ((kex->flags & KEX_INITIAL) != 0) {
if (kex->server) {
kex->ext_info_c = kexalgs_contains(peer, "ext-info-c");
kex->kex_strict = kexalgs_contains(peer,
"kex-strict-c-v00@openssh.com");
} else {
kex->ext_info_s = kexalgs_contains(peer, "ext-info-s");
kex->kex_strict = kexalgs_contains(peer,
"kex-strict-s-v00@openssh.com");
}
if (kex->kex_strict) {
debug3_f("will use strict KEX ordering");
if (seq != 0)
ssh_packet_disconnect(ssh,
"strict KEX violation: "
"KEXINIT was not the first packet");
}
}
/* Check whether client supports rsa-sha2 algorithms */

View file

@ -1,4 +1,4 @@
/* $OpenBSD: kex.h,v 1.119 2023/08/28 03:28:43 djm Exp $ */
/* $OpenBSD: kex.h,v 1.121 2023/12/18 14:45:49 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@ -105,6 +105,7 @@ enum kex_exchange {
#define KEX_RSA_SHA2_256_SUPPORTED 0x0008 /* only set in server for now */
#define KEX_RSA_SHA2_512_SUPPORTED 0x0010 /* only set in server for now */
#define KEX_HAS_PING 0x0020
#define KEX_HAS_EXT_INFO_IN_AUTH 0x0040
struct sshenc {
char *name;
@ -142,6 +143,9 @@ struct kex {
u_int kex_type;
char *server_sig_algs;
int ext_info_c;
int ext_info_s;
int kex_strict;
int ext_info_received;
struct sshbuf *my;
struct sshbuf *peer;
struct sshbuf *client_version;
@ -201,6 +205,8 @@ int kex_protocol_error(int, u_int32_t, struct ssh *);
int kex_derive_keys(struct ssh *, u_char *, u_int, const struct sshbuf *);
int kex_send_newkeys(struct ssh *);
int kex_start_rekex(struct ssh *);
int kex_server_update_ext_info(struct ssh *);
void kex_set_server_sig_algs(struct ssh *, const char *);
int kexgex_client(struct ssh *);
int kexgex_server(struct ssh *);

View file

@ -1,4 +1,4 @@
/* $OpenBSD: monitor_wrap.c,v 1.128 2023/03/31 00:44:29 dtucker Exp $ */
/* $OpenBSD: monitor_wrap.c,v 1.129 2023/12/18 14:45:49 djm Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* Copyright 2002 Markus Friedl <markus@openbsd.org>
@ -327,8 +327,8 @@ out:
log_verbose_add(options.log_verbose[i]);
process_permitopen(ssh, &options);
process_channel_timeouts(ssh, &options);
kex_set_server_sig_algs(ssh, options.pubkey_accepted_algos);
free(newopts);
sshbuf_free(m);
return (pw);

View file

@ -1,4 +1,4 @@
/* $OpenBSD: packet.c,v 1.312 2023/08/28 03:31:16 djm Exp $ */
/* $OpenBSD: packet.c,v 1.313 2023/12/18 14:45:17 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -1187,8 +1187,13 @@ ssh_packet_send2_wrapped(struct ssh *ssh)
sshbuf_dump(state->output, stderr);
#endif
/* increment sequence number for outgoing packets */
if (++state->p_send.seqnr == 0)
if (++state->p_send.seqnr == 0) {
if ((ssh->kex->flags & KEX_INITIAL) != 0) {
ssh_packet_disconnect(ssh, "outgoing sequence number "
"wrapped during initial key exchange");
}
logit("outgoing seqnr wraps around");
}
if (++state->p_send.packets == 0)
if (!(ssh->compat & SSH_BUG_NOREKEY))
return SSH_ERR_NEED_REKEY;
@ -1196,6 +1201,11 @@ ssh_packet_send2_wrapped(struct ssh *ssh)
state->p_send.bytes += len;
sshbuf_reset(state->outgoing_packet);
if (type == SSH2_MSG_NEWKEYS && ssh->kex->kex_strict) {
debug_f("resetting send seqnr %u", state->p_send.seqnr);
state->p_send.seqnr = 0;
}
if (type == SSH2_MSG_NEWKEYS)
r = ssh_set_newkeys(ssh, MODE_OUT);
else if (type == SSH2_MSG_USERAUTH_SUCCESS && state->server_side)
@ -1324,8 +1334,7 @@ ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
/* Stay in the loop until we have received a complete packet. */
for (;;) {
/* Try to read a packet from the buffer. */
r = ssh_packet_read_poll_seqnr(ssh, typep, seqnr_p);
if (r != 0)
if ((r = ssh_packet_read_poll_seqnr(ssh, typep, seqnr_p)) != 0)
break;
/* If we got a packet, return it. */
if (*typep != SSH_MSG_NONE)
@ -1395,29 +1404,6 @@ ssh_packet_read(struct ssh *ssh)
return type;
}
/*
* Waits until a packet has been received, verifies that its type matches
* that given, and gives a fatal error and exits if there is a mismatch.
*/
int
ssh_packet_read_expect(struct ssh *ssh, u_int expected_type)
{
int r;
u_char type;
if ((r = ssh_packet_read_seqnr(ssh, &type, NULL)) != 0)
return r;
if (type != expected_type) {
if ((r = sshpkt_disconnect(ssh,
"Protocol error: expected packet type %d, got %d",
expected_type, type)) != 0)
return r;
return SSH_ERR_PROTOCOL_ERROR;
}
return 0;
}
static int
ssh_packet_read_poll2_mux(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
{
@ -1608,10 +1594,16 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
if ((r = sshbuf_consume(state->input, mac->mac_len)) != 0)
goto out;
}
if (seqnr_p != NULL)
*seqnr_p = state->p_read.seqnr;
if (++state->p_read.seqnr == 0)
if (++state->p_read.seqnr == 0) {
if ((ssh->kex->flags & KEX_INITIAL) != 0) {
ssh_packet_disconnect(ssh, "incoming sequence number "
"wrapped during initial key exchange");
}
logit("incoming seqnr wraps around");
}
if (++state->p_read.packets == 0)
if (!(ssh->compat & SSH_BUG_NOREKEY))
return SSH_ERR_NEED_REKEY;
@ -1677,6 +1669,10 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
#endif
/* reset for next packet */
state->packlen = 0;
if (*typep == SSH2_MSG_NEWKEYS && ssh->kex->kex_strict) {
debug_f("resetting read seqnr %u", state->p_read.seqnr);
state->p_read.seqnr = 0;
}
if ((r = ssh_packet_check_rekey(ssh)) != 0)
return r;
@ -1699,10 +1695,39 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
r = ssh_packet_read_poll2(ssh, typep, seqnr_p);
if (r != 0)
return r;
if (*typep) {
state->keep_alive_timeouts = 0;
DBG(debug("received packet type %d", *typep));
if (*typep == 0) {
/* no message ready */
return 0;
}
state->keep_alive_timeouts = 0;
DBG(debug("received packet type %d", *typep));
/* Always process disconnect messages */
if (*typep == SSH2_MSG_DISCONNECT) {
if ((r = sshpkt_get_u32(ssh, &reason)) != 0 ||
(r = sshpkt_get_string(ssh, &msg, NULL)) != 0)
return r;
/* Ignore normal client exit notifications */
do_log2(ssh->state->server_side &&
reason == SSH2_DISCONNECT_BY_APPLICATION ?
SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_ERROR,
"Received disconnect from %s port %d:"
"%u: %.400s", ssh_remote_ipaddr(ssh),
ssh_remote_port(ssh), reason, msg);
free(msg);
return SSH_ERR_DISCONNECTED;
}
/*
* Do not implicitly handle any messages here during initial
* KEX when in strict mode. They will be need to be allowed
* explicitly by the KEX dispatch table or they will generate
* protocol errors.
*/
if (ssh->kex != NULL &&
(ssh->kex->flags & KEX_INITIAL) && ssh->kex->kex_strict)
return 0;
/* Implicitly handle transport-level messages */
switch (*typep) {
case SSH2_MSG_IGNORE:
debug3("Received SSH2_MSG_IGNORE");
@ -1717,19 +1742,6 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
debug("Remote: %.900s", msg);
free(msg);
break;
case SSH2_MSG_DISCONNECT:
if ((r = sshpkt_get_u32(ssh, &reason)) != 0 ||
(r = sshpkt_get_string(ssh, &msg, NULL)) != 0)
return r;
/* Ignore normal client exit notifications */
do_log2(ssh->state->server_side &&
reason == SSH2_DISCONNECT_BY_APPLICATION ?
SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_ERROR,
"Received disconnect from %s port %d:"
"%u: %.400s", ssh_remote_ipaddr(ssh),
ssh_remote_port(ssh), reason, msg);
free(msg);
return SSH_ERR_DISCONNECTED;
case SSH2_MSG_UNIMPLEMENTED:
if ((r = sshpkt_get_u32(ssh, &seqnr)) != 0)
return r;
@ -2219,6 +2231,7 @@ kex_to_blob(struct sshbuf *m, struct kex *kex)
(r = sshbuf_put_u32(m, kex->hostkey_type)) != 0 ||
(r = sshbuf_put_u32(m, kex->hostkey_nid)) != 0 ||
(r = sshbuf_put_u32(m, kex->kex_type)) != 0 ||
(r = sshbuf_put_u32(m, kex->kex_strict)) != 0 ||
(r = sshbuf_put_stringb(m, kex->my)) != 0 ||
(r = sshbuf_put_stringb(m, kex->peer)) != 0 ||
(r = sshbuf_put_stringb(m, kex->client_version)) != 0 ||
@ -2381,6 +2394,7 @@ kex_from_blob(struct sshbuf *m, struct kex **kexp)
(r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_type)) != 0 ||
(r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_nid)) != 0 ||
(r = sshbuf_get_u32(m, &kex->kex_type)) != 0 ||
(r = sshbuf_get_u32(m, &kex->kex_strict)) != 0 ||
(r = sshbuf_get_stringb(m, kex->my)) != 0 ||
(r = sshbuf_get_stringb(m, kex->peer)) != 0 ||
(r = sshbuf_get_stringb(m, kex->client_version)) != 0 ||
@ -2705,6 +2719,7 @@ sshpkt_disconnect(struct ssh *ssh, const char *fmt,...)
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
debug2_f("sending SSH2_MSG_DISCONNECT: %s", buf);
if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 ||
(r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_PROTOCOL_ERROR)) != 0 ||
(r = sshpkt_put_cstring(ssh, buf)) != 0 ||

View file

@ -1,4 +1,4 @@
/* $OpenBSD: packet.h,v 1.95 2023/08/28 03:31:16 djm Exp $ */
/* $OpenBSD: packet.h,v 1.96 2023/12/18 14:45:17 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -118,7 +118,6 @@ int ssh_packet_send2_wrapped(struct ssh *);
int ssh_packet_send2(struct ssh *);
int ssh_packet_read(struct ssh *);
int ssh_packet_read_expect(struct ssh *, u_int type);
int ssh_packet_read_poll(struct ssh *);
int ssh_packet_read_poll2(struct ssh *, u_char *, u_int32_t *seqnr_p);
int ssh_packet_process_incoming(struct ssh *, const char *buf, u_int len);

View file

@ -1,4 +1,4 @@
.\" $OpenBSD: ssh-add.1,v 1.84 2022/02/04 02:49:17 dtucker Exp $
.\" $OpenBSD: ssh-add.1,v 1.85 2023/12/18 14:46:56 djm Exp $
.\"
.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -35,7 +35,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd $Mdocdate: February 4 2022 $
.Dd $Mdocdate: December 18 2023 $
.Dt SSH-ADD 1
.Os
.Sh NAME
@ -43,7 +43,7 @@
.Nd adds private key identities to the OpenSSH authentication agent
.Sh SYNOPSIS
.Nm ssh-add
.Op Fl cDdKkLlqvXx
.Op Fl cCDdKkLlqvXx
.Op Fl E Ar fingerprint_hash
.Op Fl H Ar hostkey_file
.Op Fl h Ar destination_constraint
@ -52,6 +52,8 @@
.Op Ar
.Nm ssh-add
.Fl s Ar pkcs11
.Op Fl vC
.Op Ar certificate ...
.Nm ssh-add
.Fl e Ar pkcs11
.Nm ssh-add
@ -100,6 +102,9 @@ Confirmation is performed by
Successful confirmation is signaled by a zero exit status from
.Xr ssh-askpass 1 ,
rather than text entered into the requester.
.It Fl C
When loading keys into or deleting keys from the agent, process
certificates only and skip plain keys.
.It Fl D
Deletes all identities from the agent.
.It Fl d
@ -228,6 +233,9 @@ internal USB HID support.
.It Fl s Ar pkcs11
Add keys provided by the PKCS#11 shared library
.Ar pkcs11 .
Certificate files may optionally be listed as command-line arguments.
If these are present, then they will be loaded into the agent using any
corresponding private keys loaded from the PKCS#11 token.
.It Fl T Ar pubkey ...
Tests whether the private keys that correspond to the specified
.Ar pubkey

View file

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-add.c,v 1.168 2023/07/06 22:17:59 dtucker Exp $ */
/* $OpenBSD: ssh-add.c,v 1.169 2023/12/18 14:46:56 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -124,7 +124,7 @@ delete_one(int agent_fd, const struct sshkey *key, const char *comment,
}
static int
delete_stdin(int agent_fd, int qflag)
delete_stdin(int agent_fd, int qflag, int key_only, int cert_only)
{
char *line = NULL, *cp;
size_t linesize = 0;
@ -145,8 +145,13 @@ delete_stdin(int agent_fd, int qflag)
error_r(r, "(stdin):%d: invalid key", lnum);
continue;
}
if (delete_one(agent_fd, key, cp, "(stdin)", qflag) == 0)
ret = 0;
if ((!key_only && !cert_only) ||
(key_only && !sshkey_is_cert(key)) ||
(cert_only && sshkey_is_cert(key))) {
if (delete_one(agent_fd, key, cp,
"(stdin)", qflag) == 0)
ret = 0;
}
}
sshkey_free(key);
free(line);
@ -154,21 +159,26 @@ delete_stdin(int agent_fd, int qflag)
}
static int
delete_file(int agent_fd, const char *filename, int key_only, int qflag)
delete_file(int agent_fd, const char *filename, int key_only,
int cert_only, int qflag)
{
struct sshkey *public, *cert = NULL;
char *certpath = NULL, *comment = NULL;
int r, ret = -1;
if (strcmp(filename, "-") == 0)
return delete_stdin(agent_fd, qflag);
return delete_stdin(agent_fd, qflag, key_only, cert_only);
if ((r = sshkey_load_public(filename, &public, &comment)) != 0) {
printf("Bad key file %s: %s\n", filename, ssh_err(r));
return -1;
}
if (delete_one(agent_fd, public, comment, filename, qflag) == 0)
ret = 0;
if ((!key_only && !cert_only) ||
(key_only && !sshkey_is_cert(public)) ||
(cert_only && sshkey_is_cert(public))) {
if (delete_one(agent_fd, public, comment, filename, qflag) == 0)
ret = 0;
}
if (key_only)
goto out;
@ -224,8 +234,9 @@ delete_all(int agent_fd, int qflag)
}
static int
add_file(int agent_fd, const char *filename, int key_only, int qflag,
const char *skprovider, struct dest_constraint **dest_constraints,
add_file(int agent_fd, const char *filename, int key_only, int cert_only,
int qflag, const char *skprovider,
struct dest_constraint **dest_constraints,
size_t ndest_constraints)
{
struct sshkey *private, *cert;
@ -354,7 +365,8 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag,
skprovider = NULL;
}
if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
if (!cert_only &&
(r = ssh_add_identity_constrained(agent_fd, private, comment,
lifetime, confirm, maxsign, skprovider,
dest_constraints, ndest_constraints)) == 0) {
ret = 0;
@ -383,7 +395,8 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag,
xasprintf(&certpath, "%s-cert.pub", filename);
if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) {
if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT)
error_r(r, "Failed to load certificate \"%s\"", certpath);
error_r(r, "Failed to load certificate \"%s\"",
certpath);
goto out;
}
@ -438,11 +451,16 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag,
static int
update_card(int agent_fd, int add, const char *id, int qflag,
struct dest_constraint **dest_constraints, size_t ndest_constraints)
int key_only, int cert_only,
struct dest_constraint **dest_constraints, size_t ndest_constraints,
struct sshkey **certs, size_t ncerts)
{
char *pin = NULL;
int r, ret = -1;
if (key_only)
ncerts = 0;
if (add) {
if ((pin = read_passphrase("Enter passphrase for PKCS#11: ",
RP_ALLOW_STDIN)) == NULL)
@ -450,7 +468,8 @@ update_card(int agent_fd, int add, const char *id, int qflag,
}
if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin,
lifetime, confirm, dest_constraints, ndest_constraints)) == 0) {
lifetime, confirm, dest_constraints, ndest_constraints,
cert_only, certs, ncerts)) == 0) {
ret = 0;
if (!qflag) {
fprintf(stderr, "Card %s: %s\n",
@ -626,16 +645,17 @@ load_resident_keys(int agent_fd, const char *skprovider, int qflag,
}
static int
do_file(int agent_fd, int deleting, int key_only, char *file, int qflag,
const char *skprovider, struct dest_constraint **dest_constraints,
size_t ndest_constraints)
do_file(int agent_fd, int deleting, int key_only, int cert_only,
char *file, int qflag, const char *skprovider,
struct dest_constraint **dest_constraints, size_t ndest_constraints)
{
if (deleting) {
if (delete_file(agent_fd, file, key_only, qflag) == -1)
if (delete_file(agent_fd, file, key_only,
cert_only, qflag) == -1)
return -1;
} else {
if (add_file(agent_fd, file, key_only, qflag, skprovider,
dest_constraints, ndest_constraints) == -1)
if (add_file(agent_fd, file, key_only, cert_only, qflag,
skprovider, dest_constraints, ndest_constraints) == -1)
return -1;
}
return 0;
@ -783,12 +803,14 @@ main(int argc, char **argv)
int agent_fd;
char *pkcs11provider = NULL, *skprovider = NULL;
char **dest_constraint_strings = NULL, **hostkey_files = NULL;
int r, i, ch, deleting = 0, ret = 0, key_only = 0, do_download = 0;
int xflag = 0, lflag = 0, Dflag = 0, qflag = 0, Tflag = 0;
int r, i, ch, deleting = 0, ret = 0, key_only = 0, cert_only = 0;
int do_download = 0, xflag = 0, lflag = 0, Dflag = 0;
int qflag = 0, Tflag = 0;
SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
LogLevel log_level = SYSLOG_LEVEL_INFO;
struct sshkey *k, **certs = NULL;
struct dest_constraint **dest_constraints = NULL;
size_t ndest_constraints = 0;
size_t ndest_constraints = 0i, ncerts = 0;
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
sanitise_stdfd();
@ -815,7 +837,7 @@ main(int argc, char **argv)
skprovider = getenv("SSH_SK_PROVIDER");
while ((ch = getopt(argc, argv, "vkKlLcdDTxXE:e:h:H:M:m:qs:S:t:")) != -1) {
while ((ch = getopt(argc, argv, "vkKlLCcdDTxXE:e:h:H:M:m:qs:S:t:")) != -1) {
switch (ch) {
case 'v':
if (log_level == SYSLOG_LEVEL_INFO)
@ -837,6 +859,9 @@ main(int argc, char **argv)
case 'k':
key_only = 1;
break;
case 'C':
cert_only = 1;
break;
case 'K':
do_download = 1;
break;
@ -952,8 +977,19 @@ main(int argc, char **argv)
goto done;
}
if (pkcs11provider != NULL) {
for (i = 0; i < argc; i++) {
if ((r = sshkey_load_public(argv[i], &k, NULL)) != 0)
fatal_fr(r, "load certificate %s", argv[i]);
certs = xrecallocarray(certs, ncerts, ncerts + 1,
sizeof(*certs));
debug2("%s: %s", argv[i], sshkey_ssh_name(k));
certs[ncerts++] = k;
}
debug2_f("loaded %zu certificates", ncerts);
if (update_card(agent_fd, !deleting, pkcs11provider,
qflag, dest_constraints, ndest_constraints) == -1)
qflag, key_only, cert_only,
dest_constraints, ndest_constraints,
certs, ncerts) == -1)
ret = 1;
goto done;
}
@ -983,8 +1019,8 @@ main(int argc, char **argv)
default_files[i]);
if (stat(buf, &st) == -1)
continue;
if (do_file(agent_fd, deleting, key_only, buf,
qflag, skprovider,
if (do_file(agent_fd, deleting, key_only, cert_only,
buf, qflag, skprovider,
dest_constraints, ndest_constraints) == -1)
ret = 1;
else
@ -994,7 +1030,7 @@ main(int argc, char **argv)
ret = 1;
} else {
for (i = 0; i < argc; i++) {
if (do_file(agent_fd, deleting, key_only,
if (do_file(agent_fd, deleting, key_only, cert_only,
argv[i], qflag, skprovider,
dest_constraints, ndest_constraints) == -1)
ret = 1;

View file

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-agent.c,v 1.300 2023/07/19 13:56:33 djm Exp $ */
/* $OpenBSD: ssh-agent.c,v 1.304 2023/12/18 15:58:56 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -92,6 +92,8 @@
#define AGENT_MAX_SID_LEN 128
/* Maximum number of destination constraints to accept on a key */
#define AGENT_MAX_DEST_CONSTRAINTS 1024
/* Maximum number of associated certificate constraints to accept on a key */
#define AGENT_MAX_EXT_CERTS 1024
/* XXX store hostkey_sid in a refcounted tree */
@ -115,6 +117,7 @@ typedef struct socket_entry {
struct sshbuf *request;
size_t nsession_ids;
struct hostkey_sid *session_ids;
int session_bind_attempted;
} SocketEntry;
u_int sockets_alloc = 0;
@ -234,6 +237,91 @@ free_dest_constraints(struct dest_constraint *dcs, size_t ndcs)
free(dcs);
}
static void
dup_dest_constraint_hop(const struct dest_constraint_hop *dch,
struct dest_constraint_hop *out)
{
u_int i;
int r;
out->user = dch->user == NULL ? NULL : xstrdup(dch->user);
out->hostname = dch->hostname == NULL ? NULL : xstrdup(dch->hostname);
out->is_ca = dch->is_ca;
out->nkeys = dch->nkeys;
out->keys = out->nkeys == 0 ? NULL :
xcalloc(out->nkeys, sizeof(*out->keys));
out->key_is_ca = out->nkeys == 0 ? NULL :
xcalloc(out->nkeys, sizeof(*out->key_is_ca));
for (i = 0; i < dch->nkeys; i++) {
if (dch->keys[i] != NULL &&
(r = sshkey_from_private(dch->keys[i],
&(out->keys[i]))) != 0)
fatal_fr(r, "copy key");
out->key_is_ca[i] = dch->key_is_ca[i];
}
}
static struct dest_constraint *
dup_dest_constraints(const struct dest_constraint *dcs, size_t ndcs)
{
size_t i;
struct dest_constraint *ret;
if (ndcs == 0)
return NULL;
ret = xcalloc(ndcs, sizeof(*ret));
for (i = 0; i < ndcs; i++) {
dup_dest_constraint_hop(&dcs[i].from, &ret[i].from);
dup_dest_constraint_hop(&dcs[i].to, &ret[i].to);
}
return ret;
}
#ifdef DEBUG_CONSTRAINTS
static void
dump_dest_constraint_hop(const struct dest_constraint_hop *dch)
{
u_int i;
char *fp;
debug_f("user %s hostname %s is_ca %d nkeys %u",
dch->user == NULL ? "(null)" : dch->user,
dch->hostname == NULL ? "(null)" : dch->hostname,
dch->is_ca, dch->nkeys);
for (i = 0; i < dch->nkeys; i++) {
fp = NULL;
if (dch->keys[i] != NULL &&
(fp = sshkey_fingerprint(dch->keys[i],
SSH_FP_HASH_DEFAULT, SSH_FP_DEFAULT)) == NULL)
fatal_f("fingerprint failed");
debug_f("key %u/%u: %s%s%s key_is_ca %d", i, dch->nkeys,
dch->keys[i] == NULL ? "" : sshkey_ssh_name(dch->keys[i]),
dch->keys[i] == NULL ? "" : " ",
dch->keys[i] == NULL ? "none" : fp,
dch->key_is_ca[i]);
free(fp);
}
}
#endif /* DEBUG_CONSTRAINTS */
static void
dump_dest_constraints(const char *context,
const struct dest_constraint *dcs, size_t ndcs)
{
#ifdef DEBUG_CONSTRAINTS
size_t i;
debug_f("%s: %zu constraints", context, ndcs);
for (i = 0; i < ndcs; i++) {
debug_f("constraint %zu / %zu: from: ", i, ndcs);
dump_dest_constraint_hop(&dcs[i].from);
debug_f("constraint %zu / %zu: to: ", i, ndcs);
dump_dest_constraint_hop(&dcs[i].to);
}
debug_f("done for %s", context);
#endif /* DEBUG_CONSTRAINTS */
}
static void
free_identity(Identity *id)
{
@ -377,6 +465,10 @@ identity_permitted(Identity *id, SocketEntry *e, char *user,
e->nsession_ids, id->ndest_constraints);
if (id->ndest_constraints == 0)
return 0; /* unconstrained */
if (e->session_bind_attempted && e->nsession_ids == 0) {
error_f("previous session bind failed on socket");
return -1;
}
if (e->nsession_ids == 0)
return 0; /* local use */
/*
@ -456,6 +548,12 @@ identity_permitted(Identity *id, SocketEntry *e, char *user,
return 0;
}
static int
socket_is_remote(SocketEntry *e)
{
return e->session_bind_attempted || (e->nsession_ids != 0);
}
/* return matching private key for given public key */
static Identity *
lookup_identity(struct sshkey *key)
@ -505,13 +603,22 @@ process_request_identities(SocketEntry *e)
Identity *id;
struct sshbuf *msg, *keys;
int r;
u_int nentries = 0;
u_int i = 0, nentries = 0;
char *fp;
debug2_f("entering");
if ((msg = sshbuf_new()) == NULL || (keys = sshbuf_new()) == NULL)
fatal_f("sshbuf_new failed");
TAILQ_FOREACH(id, &idtab->idlist, next) {
if ((fp = sshkey_fingerprint(id->key, SSH_FP_HASH_DEFAULT,
SSH_FP_DEFAULT)) == NULL)
fatal_f("fingerprint failed");
debug_f("key %u / %u: %s %s", i++, idtab->nentries,
sshkey_ssh_name(id->key), fp);
dump_dest_constraints(__func__,
id->dest_constraints, id->ndest_constraints);
free(fp);
/* identity not visible, don't include in response */
if (identity_permitted(id, e, NULL, NULL, NULL) != 0)
continue;
@ -1051,11 +1158,14 @@ parse_dest_constraint(struct sshbuf *m, struct dest_constraint *dc)
static int
parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
struct dest_constraint **dcsp, size_t *ndcsp)
struct dest_constraint **dcsp, size_t *ndcsp, int *cert_onlyp,
struct sshkey ***certs, size_t *ncerts)
{
char *ext_name = NULL;
int r;
struct sshbuf *b = NULL;
u_char v;
struct sshkey *k;
if ((r = sshbuf_get_cstring(m, &ext_name, NULL)) != 0) {
error_fr(r, "parse constraint extension");
@ -1098,6 +1208,36 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
*dcsp + (*ndcsp)++)) != 0)
goto out; /* error already logged */
}
} else if (strcmp(ext_name,
"associated-certs-v00@openssh.com") == 0) {
if (certs == NULL || ncerts == NULL || cert_onlyp == NULL) {
error_f("%s not valid here", ext_name);
r = SSH_ERR_INVALID_FORMAT;
goto out;
}
if (*certs != NULL) {
error_f("%s already set", ext_name);
goto out;
}
if ((r = sshbuf_get_u8(m, &v)) != 0 ||
(r = sshbuf_froms(m, &b)) != 0) {
error_fr(r, "parse %s", ext_name);
goto out;
}
*cert_onlyp = v != 0;
while (sshbuf_len(b) != 0) {
if (*ncerts >= AGENT_MAX_EXT_CERTS) {
error_f("too many %s constraints", ext_name);
goto out;
}
*certs = xrecallocarray(*certs, *ncerts, *ncerts + 1,
sizeof(**certs));
if ((r = sshkey_froms(b, &k)) != 0) {
error_fr(r, "parse key");
goto out;
}
(*certs)[(*ncerts)++] = k;
}
} else {
error_f("unsupported constraint \"%s\"", ext_name);
r = SSH_ERR_FEATURE_UNSUPPORTED;
@ -1114,7 +1254,8 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
static int
parse_key_constraints(struct sshbuf *m, struct sshkey *k, time_t *deathp,
u_int *secondsp, int *confirmp, char **sk_providerp,
struct dest_constraint **dcsp, size_t *ndcsp)
struct dest_constraint **dcsp, size_t *ndcsp,
int *cert_onlyp, size_t *ncerts, struct sshkey ***certs)
{
u_char ctype;
int r;
@ -1169,7 +1310,8 @@ parse_key_constraints(struct sshbuf *m, struct sshkey *k, time_t *deathp,
break;
case SSH_AGENT_CONSTRAIN_EXTENSION:
if ((r = parse_key_constraint_extension(m,
sk_providerp, dcsp, ndcsp)) != 0)
sk_providerp, dcsp, ndcsp,
cert_onlyp, certs, ncerts)) != 0)
goto out; /* error already logged */
break;
default:
@ -1206,11 +1348,13 @@ process_add_identity(SocketEntry *e)
goto out;
}
if (parse_key_constraints(e->request, k, &death, &seconds, &confirm,
&sk_provider, &dest_constraints, &ndest_constraints) != 0) {
&sk_provider, &dest_constraints, &ndest_constraints,
NULL, NULL, NULL) != 0) {
error_f("failed to parse constraints");
sshbuf_reset(e->request);
goto out;
}
dump_dest_constraints(__func__, dest_constraints, ndest_constraints);
if (sk_provider != NULL) {
if (!sshkey_is_sk(k)) {
@ -1221,7 +1365,7 @@ process_add_identity(SocketEntry *e)
if (strcasecmp(sk_provider, "internal") == 0) {
debug_f("internal provider");
} else {
if (e->nsession_ids != 0 && !remote_add_provider) {
if (socket_is_remote(e) && !remote_add_provider) {
verbose("failed add of SK provider \"%.100s\": "
"remote addition of providers is disabled",
sk_provider);
@ -1365,6 +1509,32 @@ no_identities(SocketEntry *e)
sshbuf_free(msg);
}
/* Add an identity to idlist; takes ownership of 'key' and 'comment' */
static void
add_p11_identity(struct sshkey *key, char *comment, const char *provider,
time_t death, u_int confirm, struct dest_constraint *dest_constraints,
size_t ndest_constraints)
{
Identity *id;
if (lookup_identity(key) != NULL) {
sshkey_free(key);
free(comment);
return;
}
id = xcalloc(1, sizeof(Identity));
id->key = key;
id->comment = comment;
id->provider = xstrdup(provider);
id->death = death;
id->confirm = confirm;
id->dest_constraints = dup_dest_constraints(dest_constraints,
ndest_constraints);
id->ndest_constraints = ndest_constraints;
TAILQ_INSERT_TAIL(&idtab->idlist, id, next);
idtab->nentries++;
}
#ifdef ENABLE_PKCS11
static void
process_add_smartcard_key(SocketEntry *e)
@ -1375,9 +1545,10 @@ process_add_smartcard_key(SocketEntry *e)
u_int seconds = 0;
time_t death = 0;
struct sshkey **keys = NULL, *k;
Identity *id;
struct dest_constraint *dest_constraints = NULL;
size_t ndest_constraints = 0;
size_t j, ndest_constraints = 0, ncerts = 0;
struct sshkey **certs = NULL;
int cert_only = 0;
debug2_f("entering");
if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 ||
@ -1386,11 +1557,13 @@ process_add_smartcard_key(SocketEntry *e)
goto send;
}
if (parse_key_constraints(e->request, NULL, &death, &seconds, &confirm,
NULL, &dest_constraints, &ndest_constraints) != 0) {
NULL, &dest_constraints, &ndest_constraints, &cert_only,
&ncerts, &certs) != 0) {
error_f("failed to parse constraints");
goto send;
}
if (e->nsession_ids != 0 && !remote_add_provider) {
dump_dest_constraints(__func__, dest_constraints, ndest_constraints);
if (socket_is_remote(e) && !remote_add_provider) {
verbose("failed PKCS#11 add of \"%.100s\": remote addition of "
"providers is disabled", provider);
goto send;
@ -1411,26 +1584,28 @@ process_add_smartcard_key(SocketEntry *e)
count = pkcs11_add_provider(canonical_provider, pin, &keys, &comments);
for (i = 0; i < count; i++) {
k = keys[i];
if (lookup_identity(k) == NULL) {
id = xcalloc(1, sizeof(Identity));
id->key = k;
keys[i] = NULL; /* transferred */
id->provider = xstrdup(canonical_provider);
if (*comments[i] != '\0') {
id->comment = comments[i];
comments[i] = NULL; /* transferred */
} else {
id->comment = xstrdup(canonical_provider);
}
id->death = death;
id->confirm = confirm;
id->dest_constraints = dest_constraints;
id->ndest_constraints = ndest_constraints;
dest_constraints = NULL; /* transferred */
ndest_constraints = 0;
TAILQ_INSERT_TAIL(&idtab->idlist, id, next);
idtab->nentries++;
if (comments[i] == NULL || comments[i][0] == '\0') {
free(comments[i]);
comments[i] = xstrdup(canonical_provider);
}
for (j = 0; j < ncerts; j++) {
if (!sshkey_is_cert(certs[j]))
continue;
if (!sshkey_equal_public(keys[i], certs[j]))
continue;
if (pkcs11_make_cert(keys[i], certs[j], &k) != 0)
continue;
add_p11_identity(k, xstrdup(comments[i]),
canonical_provider, death, confirm,
dest_constraints, ndest_constraints);
success = 1;
}
if (!cert_only && lookup_identity(keys[i]) == NULL) {
add_p11_identity(keys[i], comments[i],
canonical_provider, death, confirm,
dest_constraints, ndest_constraints);
keys[i] = NULL; /* transferred */
comments[i] = NULL; /* transferred */
success = 1;
}
/* XXX update constraints for existing keys */
@ -1443,6 +1618,9 @@ send:
free(keys);
free(comments);
free_dest_constraints(dest_constraints, ndest_constraints);
for (j = 0; j < ncerts; j++)
sshkey_free(certs[j]);
free(certs);
send_status(e, success);
}
@ -1500,6 +1678,7 @@ process_ext_session_bind(SocketEntry *e)
u_char fwd = 0;
debug2_f("entering");
e->session_bind_attempted = 1;
if ((r = sshkey_froms(e->request, &key)) != 0 ||
(r = sshbuf_froms(e->request, &sid)) != 0 ||
(r = sshbuf_froms(e->request, &sig)) != 0 ||

View file

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-pkcs11-client.c,v 1.18 2023/07/19 14:03:45 djm Exp $ */
/* $OpenBSD: ssh-pkcs11-client.c,v 1.19 2023/12/18 14:46:56 djm Exp $ */
/*
* Copyright (c) 2010 Markus Friedl. All rights reserved.
* Copyright (c) 2014 Pedro Martelletto. All rights reserved.
@ -405,6 +405,60 @@ wrap_key(struct helper *helper, struct sshkey *k)
helper->path, helper->nrsa, helper->nec);
}
/*
* Make a private PKCS#11-backed certificate by grafting a previously-loaded
* PKCS#11 private key and a public certificate key.
*/
int
pkcs11_make_cert(const struct sshkey *priv,
const struct sshkey *certpub, struct sshkey **certprivp)
{
struct helper *helper = NULL;
struct sshkey *ret;
int r;
debug3_f("private key type %s cert type %s", sshkey_type(priv),
sshkey_type(certpub));
*certprivp = NULL;
if (!sshkey_is_cert(certpub) || sshkey_is_cert(priv) ||
!sshkey_equal_public(priv, certpub)) {
error_f("private key %s doesn't match cert %s",
sshkey_type(priv), sshkey_type(certpub));
return SSH_ERR_INVALID_ARGUMENT;
}
*certprivp = NULL;
if (priv->type == KEY_RSA) {
if ((helper = helper_by_rsa(priv->rsa)) == NULL ||
helper->fd == -1)
fatal_f("no helper for PKCS11 RSA key");
if ((r = sshkey_from_private(priv, &ret)) != 0)
fatal_fr(r, "copy key");
RSA_set_method(ret->rsa, helper->rsa_meth);
if (helper->nrsa++ >= INT_MAX)
fatal_f("RSA refcount error");
} else if (priv->type == KEY_ECDSA) {
if ((helper = helper_by_ec(priv->ecdsa)) == NULL ||
helper->fd == -1)
fatal_f("no helper for PKCS11 EC key");
if ((r = sshkey_from_private(priv, &ret)) != 0)
fatal_fr(r, "copy key");
EC_KEY_set_method(ret->ecdsa, helper->ec_meth);
if (helper->nec++ >= INT_MAX)
fatal_f("EC refcount error");
} else
fatal_f("unknown key type %s", sshkey_type(priv));
ret->flags |= SSHKEY_FLAG_EXT;
if ((r = sshkey_to_certified(ret)) != 0 ||
(r = sshkey_cert_copy(certpub, ret)) != 0)
fatal_fr(r, "graft certificate");
debug3_f("provider %s remaining keys: %zu RSA %zu ECDSA",
helper->path, helper->nrsa, helper->nec);
/* success */
*certprivp = ret;
return 0;
}
static int
pkcs11_start_helper_methods(struct helper *helper)
{

View file

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-pkcs11.h,v 1.6 2020/01/25 00:03:36 djm Exp $ */
/* $OpenBSD: ssh-pkcs11.h,v 1.7 2023/12/18 14:46:56 djm Exp $ */
/*
* Copyright (c) 2010 Markus Friedl. All rights reserved.
*
@ -35,6 +35,9 @@ struct sshkey *
u_int32_t *);
#endif
/* Only available in ssh-pkcs11-client.c so far */
int pkcs11_make_cert(const struct sshkey *,
const struct sshkey *, struct sshkey **);
#if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11)
#undef ENABLE_PKCS11
#endif

View file

@ -1,4 +1,4 @@
/* $OpenBSD: ssh.c,v 1.598 2023/10/12 02:48:43 djm Exp $ */
/* $OpenBSD: ssh.c,v 1.599 2023/12/18 14:47:44 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -610,6 +610,41 @@ ssh_conn_info_free(struct ssh_conn_info *cinfo)
free(cinfo);
}
static int
valid_hostname(const char *s)
{
size_t i;
if (*s == '-')
return 0;
for (i = 0; s[i] != 0; i++) {
if (strchr("'`\"$\\;&<>|(){}", s[i]) != NULL ||
isspace((u_char)s[i]) || iscntrl((u_char)s[i]))
return 0;
}
return 1;
}
static int
valid_ruser(const char *s)
{
size_t i;
if (*s == '-')
return 0;
for (i = 0; s[i] != 0; i++) {
if (strchr("'`\";&<>|(){}", s[i]) != NULL)
return 0;
/* Disallow '-' after whitespace */
if (isspace((u_char)s[i]) && s[i + 1] == '-')
return 0;
/* Disallow \ in last position */
if (s[i] == '\\' && s[i + 1] == '\0')
return 0;
}
return 1;
}
/*
* Main program for the ssh client.
*/
@ -1092,6 +1127,10 @@ main(int ac, char **av)
if (!host)
usage();
if (!valid_hostname(host))
fatal("hostname contains invalid characters");
if (options.user != NULL && !valid_ruser(options.user))
fatal("remote username contains invalid characters");
options.host_arg = xstrdup(host);
#ifdef WITH_OPENSSL

View file

@ -1,4 +1,4 @@
/* $OpenBSD: sshconnect2.c,v 1.369 2023/12/13 03:28:19 djm Exp $ */
/* $OpenBSD: sshconnect2.c,v 1.371 2023/12/18 14:45:49 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Damien Miller. All rights reserved.
@ -351,7 +351,6 @@ struct cauthmethod {
};
static int input_userauth_service_accept(int, u_int32_t, struct ssh *);
static int input_userauth_ext_info(int, u_int32_t, struct ssh *);
static int input_userauth_success(int, u_int32_t, struct ssh *);
static int input_userauth_failure(int, u_int32_t, struct ssh *);
static int input_userauth_banner(int, u_int32_t, struct ssh *);
@ -453,10 +452,8 @@ ssh_userauth2(struct ssh *ssh, const char *local_user,
authctxt.mech_tried = 0;
#endif
authctxt.agent_fd = -1;
pubkey_prepare(ssh, &authctxt);
if (authctxt.method == NULL) {
if (authctxt.method == NULL)
fatal_f("internal error: cannot send userauth none request");
}
if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_REQUEST)) != 0 ||
(r = sshpkt_put_cstring(ssh, "ssh-userauth")) != 0 ||
@ -465,7 +462,7 @@ ssh_userauth2(struct ssh *ssh, const char *local_user,
ssh->authctxt = &authctxt;
ssh_dispatch_init(ssh, &input_userauth_error);
ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_ext_info);
ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, kex_input_ext_info);
ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_ACCEPT, &input_userauth_service_accept);
ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt.success); /* loop until success */
pubkey_cleanup(ssh);
@ -515,7 +512,9 @@ input_userauth_service_accept(int type, u_int32_t seq, struct ssh *ssh)
/* initial userauth request */
userauth_none(ssh);
ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_error);
/* accept EXT_INFO at any time during userauth */
ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, ssh->kex->ext_info_s ?
&kex_input_ext_info : &input_userauth_error);
ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success);
ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure);
ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner);
@ -524,12 +523,6 @@ input_userauth_service_accept(int type, u_int32_t seq, struct ssh *ssh)
return r;
}
static int
input_userauth_ext_info(int type, u_int32_t seqnr, struct ssh *ssh)
{
return kex_input_ext_info(type, seqnr, ssh);
}
void
userauth(struct ssh *ssh, char *authlist)
{
@ -608,6 +601,7 @@ input_userauth_success(int type, u_int32_t seq, struct ssh *ssh)
free(authctxt->methoddata);
authctxt->methoddata = NULL;
authctxt->success = 1; /* break out */
ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, dispatch_protocol_error);
return 0;
}
@ -1677,10 +1671,10 @@ pubkey_prepare(struct ssh *ssh, Authctxt *authctxt)
struct identity *id, *id2, *tmp;
struct idlist agent, files, *preferred;
struct sshkey *key;
int agent_fd = -1, i, r, found;
int disallowed, agent_fd = -1, i, r, found;
size_t j;
struct ssh_identitylist *idlist;
char *ident;
char *cp, *ident;
TAILQ_INIT(&agent); /* keys from the agent */
TAILQ_INIT(&files); /* keys from the config file */
@ -1798,16 +1792,30 @@ pubkey_prepare(struct ssh *ssh, Authctxt *authctxt)
TAILQ_CONCAT(preferred, &files, next);
/* finally, filter by PubkeyAcceptedAlgorithms */
TAILQ_FOREACH_SAFE(id, preferred, next, id2) {
if (id->key != NULL && !key_type_allowed_by_config(id->key)) {
debug("Skipping %s key %s - "
"corresponding algo not in PubkeyAcceptedAlgorithms",
sshkey_ssh_name(id->key), id->filename);
TAILQ_REMOVE(preferred, id, next);
sshkey_free(id->key);
free(id->filename);
memset(id, 0, sizeof(*id));
disallowed = 0;
cp = NULL;
if (id->key == NULL)
continue;
if (!key_type_allowed_by_config(id->key)) {
debug("Skipping %s key %s - corresponding algorithm "
"not in PubkeyAcceptedAlgorithms",
sshkey_ssh_name(id->key), id->filename);
disallowed = 1;
} else if (ssh->kex->server_sig_algs != NULL &&
(cp = key_sig_algorithm(ssh, id->key)) == NULL) {
debug("Skipping %s key %s - corresponding algorithm "
"not supported by server",
sshkey_ssh_name(id->key), id->filename);
disallowed = 1;
}
free(cp);
if (!disallowed)
continue;
/* remove key */
TAILQ_REMOVE(preferred, id, next);
sshkey_free(id->key);
free(id->filename);
memset(id, 0, sizeof(*id));
}
/* List the keys we plan on using */
TAILQ_FOREACH_SAFE(id, preferred, next, id2) {
@ -1853,6 +1861,12 @@ userauth_pubkey(struct ssh *ssh)
Identity *id;
int sent = 0;
char *ident;
static int prepared;
if (!prepared) {
pubkey_prepare(ssh, authctxt);
prepared = 1;
}
while ((id = TAILQ_FIRST(&authctxt->keys))) {
if (id->tried++)

View file

@ -1,4 +1,4 @@
/* $OpenBSD: sshd.c,v 1.600 2023/03/08 04:43:12 guenther Exp $ */
/* $OpenBSD: sshd.c,v 1.601 2023/12/18 14:45:49 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -2240,7 +2240,9 @@ do_ssh2_kex(struct ssh *ssh)
/* start key exchange */
if ((r = kex_setup(ssh, myproposal)) != 0)
fatal_r(r, "kex_setup");
kex_set_server_sig_algs(ssh, options.pubkey_accepted_algos);
kex = ssh->kex;
#ifdef WITH_OPENSSL
kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_server;
kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_server;

View file

@ -1,3 +1,3 @@
/* $OpenBSD: version.h,v 1.99 2023/10/04 04:04:09 djm Exp $ */
/* $OpenBSD: version.h,v 1.100 2023/12/18 14:48:44 djm Exp $ */
#define SSH_VERSION "OpenSSH_9.5"
#define SSH_VERSION "OpenSSH_9.6"