sync with OpenBSD -current
This commit is contained in:
parent
da785accdf
commit
659ea2942e
39 changed files with 1318 additions and 384 deletions
|
@ -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 $
|
||||
|
|
|
@ -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 $
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 ||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue