upstream: Introduce a transport-level ping facility
This adds a pair of SSH transport protocol messages SSH2_MSG_PING/PONG to implement a ping capability. These messages use numbers in the "local extensions" number space and are advertised using a "ping@openssh.com" ext-info message with a string version number of "0". ok markus@ OpenBSD-Commit-ID: b6b3c4cb2084c62f85a8dc67cf74954015eb547f
This commit is contained in:
parent
d2d247938b
commit
dce6d80d2e
35
PROTOCOL
35
PROTOCOL
|
@ -104,6 +104,39 @@ http://git.libssh.org/users/aris/libssh.git/plain/doc/curve25519-sha256@libssh.o
|
||||||
|
|
||||||
This is identical to curve25519-sha256 as later published in RFC8731.
|
This is identical to curve25519-sha256 as later published in RFC8731.
|
||||||
|
|
||||||
|
1.9 transport: ping facility
|
||||||
|
|
||||||
|
OpenSSH implements a transport level ping message SSH2_MSG_PING
|
||||||
|
and a corresponding SSH2_MSG_PONG reply.
|
||||||
|
|
||||||
|
#define SSH2_MSG_PING 192
|
||||||
|
#define SSH2_MSG_PONG 193
|
||||||
|
|
||||||
|
The ping message is simply:
|
||||||
|
|
||||||
|
byte SSH_MSG_PING
|
||||||
|
string data
|
||||||
|
|
||||||
|
The reply copies the data (which may be the empty string) from the
|
||||||
|
ping:
|
||||||
|
|
||||||
|
byte SSH_MSG_PONG
|
||||||
|
string data
|
||||||
|
|
||||||
|
Replies are sent in order. They are sent immediately except when rekeying
|
||||||
|
is in progress, in which case they are queued until rekeying completes.
|
||||||
|
|
||||||
|
The server advertises support for these messages using the
|
||||||
|
SSH2_MSG_EXT_INFO mechanism (RFC8308), with the following message:
|
||||||
|
|
||||||
|
string "ping@openssh.com"
|
||||||
|
string "0" (version)
|
||||||
|
|
||||||
|
The ping/reply message is implemented at the transport layer rather
|
||||||
|
than as a named global or channel request to allow pings with very
|
||||||
|
short packet lengths, which would not be possible with other
|
||||||
|
approaches.
|
||||||
|
|
||||||
2. Connection protocol changes
|
2. Connection protocol changes
|
||||||
|
|
||||||
2.1. connection: Channel write close extension "eow@openssh.com"
|
2.1. connection: Channel write close extension "eow@openssh.com"
|
||||||
|
@ -712,4 +745,4 @@ master instance and later clients.
|
||||||
OpenSSH extends the usual agent protocol. These changes are documented
|
OpenSSH extends the usual agent protocol. These changes are documented
|
||||||
in the PROTOCOL.agent file.
|
in the PROTOCOL.agent file.
|
||||||
|
|
||||||
$OpenBSD: PROTOCOL,v 1.48 2022/11/07 01:53:01 dtucker Exp $
|
$OpenBSD: PROTOCOL,v 1.49 2023/08/28 03:28:43 djm Exp $
|
||||||
|
|
47
kex.c
47
kex.c
|
@ -1,4 +1,4 @@
|
||||||
/* $OpenBSD: kex.c,v 1.180 2023/08/21 21:16:18 tobhe Exp $ */
|
/* $OpenBSD: kex.c,v 1.181 2023/08/28 03:28:43 djm Exp $ */
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
|
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
|
||||||
*
|
*
|
||||||
|
@ -492,12 +492,14 @@ kex_send_ext_info(struct ssh *ssh)
|
||||||
return SSH_ERR_ALLOC_FAIL;
|
return SSH_ERR_ALLOC_FAIL;
|
||||||
/* XXX filter algs list by allowed pubkey/hostbased types */
|
/* XXX filter algs list by allowed pubkey/hostbased types */
|
||||||
if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
|
if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 ||
|
||||||
(r = sshpkt_put_u32(ssh, 2)) != 0 ||
|
(r = sshpkt_put_u32(ssh, 3)) != 0 ||
|
||||||
(r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
|
(r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 ||
|
||||||
(r = sshpkt_put_cstring(ssh, algs)) != 0 ||
|
(r = sshpkt_put_cstring(ssh, algs)) != 0 ||
|
||||||
(r = sshpkt_put_cstring(ssh,
|
(r = sshpkt_put_cstring(ssh,
|
||||||
"publickey-hostbound@openssh.com")) != 0 ||
|
"publickey-hostbound@openssh.com")) != 0 ||
|
||||||
(r = sshpkt_put_cstring(ssh, "0")) != 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 = sshpkt_send(ssh)) != 0) {
|
||||||
error_fr(r, "compose");
|
error_fr(r, "compose");
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -527,6 +529,23 @@ kex_send_newkeys(struct ssh *ssh)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check whether an ext_info value contains the expected version string */
|
||||||
|
static int
|
||||||
|
kex_ext_info_check_ver(struct kex *kex, const char *name,
|
||||||
|
const u_char *val, size_t len, const char *want_ver, u_int flag)
|
||||||
|
{
|
||||||
|
if (memchr(val, '\0', len) != NULL) {
|
||||||
|
error("SSH2_MSG_EXT_INFO: %s value contains nul byte", name);
|
||||||
|
return SSH_ERR_INVALID_FORMAT;
|
||||||
|
}
|
||||||
|
debug_f("%s=<%s>", name, val);
|
||||||
|
if (strcmp(val, want_ver) == 0)
|
||||||
|
kex->flags |= flag;
|
||||||
|
else
|
||||||
|
debug_f("unsupported version of %s extension", name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
|
kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
|
||||||
{
|
{
|
||||||
|
@ -557,6 +576,8 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
|
||||||
/* Ensure no \0 lurking in value */
|
/* Ensure no \0 lurking in value */
|
||||||
if (memchr(val, '\0', vlen) != NULL) {
|
if (memchr(val, '\0', vlen) != NULL) {
|
||||||
error_f("nul byte in %s", name);
|
error_f("nul byte in %s", name);
|
||||||
|
free(name);
|
||||||
|
free(val);
|
||||||
return SSH_ERR_INVALID_FORMAT;
|
return SSH_ERR_INVALID_FORMAT;
|
||||||
}
|
}
|
||||||
debug_f("%s=<%s>", name, val);
|
debug_f("%s=<%s>", name, val);
|
||||||
|
@ -564,18 +585,18 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
|
||||||
val = NULL;
|
val = NULL;
|
||||||
} else if (strcmp(name,
|
} else if (strcmp(name,
|
||||||
"publickey-hostbound@openssh.com") == 0) {
|
"publickey-hostbound@openssh.com") == 0) {
|
||||||
/* XXX refactor */
|
if ((r = kex_ext_info_check_ver(kex, name, val, vlen,
|
||||||
/* Ensure no \0 lurking in value */
|
"0", KEX_HAS_PUBKEY_HOSTBOUND)) != 0) {
|
||||||
if (memchr(val, '\0', vlen) != NULL) {
|
free(name);
|
||||||
error_f("nul byte in %s", name);
|
free(val);
|
||||||
return SSH_ERR_INVALID_FORMAT;
|
return r;
|
||||||
}
|
}
|
||||||
debug_f("%s=<%s>", name, val);
|
} else if (strcmp(name, "ping@openssh.com") == 0) {
|
||||||
if (strcmp(val, "0") == 0)
|
if ((r = kex_ext_info_check_ver(kex, name, val, vlen,
|
||||||
kex->flags |= KEX_HAS_PUBKEY_HOSTBOUND;
|
"0", KEX_HAS_PING)) != 0) {
|
||||||
else {
|
free(name);
|
||||||
debug_f("unsupported version of %s extension",
|
free(val);
|
||||||
name);
|
return r;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
debug_f("%s (unrecognised)", name);
|
debug_f("%s (unrecognised)", name);
|
||||||
|
|
3
kex.h
3
kex.h
|
@ -1,4 +1,4 @@
|
||||||
/* $OpenBSD: kex.h,v 1.118 2023/03/06 12:14:48 dtucker Exp $ */
|
/* $OpenBSD: kex.h,v 1.119 2023/08/28 03:28:43 djm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
|
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
|
||||||
|
@ -111,6 +111,7 @@ enum kex_exchange {
|
||||||
#define KEX_HAS_PUBKEY_HOSTBOUND 0x0004
|
#define KEX_HAS_PUBKEY_HOSTBOUND 0x0004
|
||||||
#define KEX_RSA_SHA2_256_SUPPORTED 0x0008 /* only set in server for now */
|
#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_RSA_SHA2_512_SUPPORTED 0x0010 /* only set in server for now */
|
||||||
|
#define KEX_HAS_PING 0x0020
|
||||||
|
|
||||||
struct sshenc {
|
struct sshenc {
|
||||||
char *name;
|
char *name;
|
||||||
|
|
23
packet.c
23
packet.c
|
@ -1,4 +1,4 @@
|
||||||
/* $OpenBSD: packet.c,v 1.310 2023/04/06 03:21:31 djm Exp $ */
|
/* $OpenBSD: packet.c,v 1.311 2023/08/28 03:28:43 djm Exp $ */
|
||||||
/*
|
/*
|
||||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||||
|
@ -1054,6 +1054,8 @@ int
|
||||||
ssh_packet_log_type(u_char type)
|
ssh_packet_log_type(u_char type)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
case SSH2_MSG_PING:
|
||||||
|
case SSH2_MSG_PONG:
|
||||||
case SSH2_MSG_CHANNEL_DATA:
|
case SSH2_MSG_CHANNEL_DATA:
|
||||||
case SSH2_MSG_CHANNEL_EXTENDED_DATA:
|
case SSH2_MSG_CHANNEL_EXTENDED_DATA:
|
||||||
case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
|
case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
|
||||||
|
@ -1675,7 +1677,7 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
|
||||||
goto out;
|
goto out;
|
||||||
if (ssh_packet_log_type(*typep))
|
if (ssh_packet_log_type(*typep))
|
||||||
debug3("receive packet: type %u", *typep);
|
debug3("receive packet: type %u", *typep);
|
||||||
if (*typep < SSH2_MSG_MIN || *typep >= SSH2_MSG_LOCAL_MIN) {
|
if (*typep < SSH2_MSG_MIN) {
|
||||||
if ((r = sshpkt_disconnect(ssh,
|
if ((r = sshpkt_disconnect(ssh,
|
||||||
"Invalid ssh2 packet type: %d", *typep)) != 0 ||
|
"Invalid ssh2 packet type: %d", *typep)) != 0 ||
|
||||||
(r = ssh_packet_write_wait(ssh)) != 0)
|
(r = ssh_packet_write_wait(ssh)) != 0)
|
||||||
|
@ -1710,6 +1712,8 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
|
||||||
u_int reason, seqnr;
|
u_int reason, seqnr;
|
||||||
int r;
|
int r;
|
||||||
u_char *msg;
|
u_char *msg;
|
||||||
|
const u_char *d;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
msg = NULL;
|
msg = NULL;
|
||||||
|
@ -1753,6 +1757,21 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
|
||||||
debug("Received SSH2_MSG_UNIMPLEMENTED for %u",
|
debug("Received SSH2_MSG_UNIMPLEMENTED for %u",
|
||||||
seqnr);
|
seqnr);
|
||||||
break;
|
break;
|
||||||
|
case SSH2_MSG_PING:
|
||||||
|
if ((r = sshpkt_get_string_direct(ssh, &d, &len)) != 0)
|
||||||
|
return r;
|
||||||
|
DBG(debug("Received SSH2_MSG_PING len %zu", len));
|
||||||
|
if ((r = sshpkt_start(ssh, SSH2_MSG_PONG)) != 0 ||
|
||||||
|
(r = sshpkt_put_string(ssh, d, len)) != 0 ||
|
||||||
|
(r = sshpkt_send(ssh)) != 0)
|
||||||
|
return r;
|
||||||
|
break;
|
||||||
|
case SSH2_MSG_PONG:
|
||||||
|
if ((r = sshpkt_get_string_direct(ssh,
|
||||||
|
NULL, &len)) != 0)
|
||||||
|
return r;
|
||||||
|
DBG(debug("Received SSH2_MSG_PONG len %zu", len));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
6
ssh2.h
6
ssh2.h
|
@ -1,4 +1,4 @@
|
||||||
/* $OpenBSD: ssh2.h,v 1.20 2023/08/14 03:37:00 djm Exp $ */
|
/* $OpenBSD: ssh2.h,v 1.21 2023/08/28 03:28:43 djm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||||
|
@ -108,6 +108,10 @@
|
||||||
#define SSH2_MSG_KEX_ECDH_INIT 30
|
#define SSH2_MSG_KEX_ECDH_INIT 30
|
||||||
#define SSH2_MSG_KEX_ECDH_REPLY 31
|
#define SSH2_MSG_KEX_ECDH_REPLY 31
|
||||||
|
|
||||||
|
/* transport layer: OpenSSH extensions */
|
||||||
|
#define SSH2_MSG_PING 192
|
||||||
|
#define SSH2_MSG_PONG 193
|
||||||
|
|
||||||
/* user authentication: generic */
|
/* user authentication: generic */
|
||||||
|
|
||||||
#define SSH2_MSG_USERAUTH_REQUEST 50
|
#define SSH2_MSG_USERAUTH_REQUEST 50
|
||||||
|
|
Loading…
Reference in New Issue