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:
djm@openbsd.org 2023-08-28 03:28:43 +00:00 committed by Damien Miller
parent d2d247938b
commit dce6d80d2e
No known key found for this signature in database
5 changed files with 96 additions and 18 deletions

View File

@ -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.
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.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
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
View File

@ -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.
*
@ -492,12 +492,14 @@ kex_send_ext_info(struct ssh *ssh)
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, 2)) != 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,
"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) {
error_fr(r, "compose");
goto out;
@ -527,6 +529,23 @@ kex_send_newkeys(struct ssh *ssh)
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
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 */
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);
@ -564,18 +585,18 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
val = NULL;
} else if (strcmp(name,
"publickey-hostbound@openssh.com") == 0) {
/* XXX refactor */
/* Ensure no \0 lurking in value */
if (memchr(val, '\0', vlen) != NULL) {
error_f("nul byte in %s", name);
return SSH_ERR_INVALID_FORMAT;
if ((r = kex_ext_info_check_ver(kex, name, val, vlen,
"0", KEX_HAS_PUBKEY_HOSTBOUND)) != 0) {
free(name);
free(val);
return r;
}
debug_f("%s=<%s>", name, val);
if (strcmp(val, "0") == 0)
kex->flags |= KEX_HAS_PUBKEY_HOSTBOUND;
else {
debug_f("unsupported version of %s extension",
name);
} 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);
return r;
}
} else
debug_f("%s (unrecognised)", name);

3
kex.h
View File

@ -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.
@ -111,6 +111,7 @@ enum kex_exchange {
#define KEX_HAS_PUBKEY_HOSTBOUND 0x0004
#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
struct sshenc {
char *name;

View File

@ -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>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -1054,6 +1054,8 @@ int
ssh_packet_log_type(u_char type)
{
switch (type) {
case SSH2_MSG_PING:
case SSH2_MSG_PONG:
case SSH2_MSG_CHANNEL_DATA:
case SSH2_MSG_CHANNEL_EXTENDED_DATA:
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;
if (ssh_packet_log_type(*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,
"Invalid ssh2 packet type: %d", *typep)) != 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;
int r;
u_char *msg;
const u_char *d;
size_t len;
for (;;) {
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",
seqnr);
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:
return 0;
}

6
ssh2.h
View File

@ -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.
@ -108,6 +108,10 @@
#define SSH2_MSG_KEX_ECDH_INIT 30
#define SSH2_MSG_KEX_ECDH_REPLY 31
/* transport layer: OpenSSH extensions */
#define SSH2_MSG_PING 192
#define SSH2_MSG_PONG 193
/* user authentication: generic */
#define SSH2_MSG_USERAUTH_REQUEST 50