upstream: remove this KEX fuzzer; it's awkward to use and doesn't play

nice with popular fuzzing drivers like libfuzzer. AFAIK nobody has used it
but me.

OpenBSD-Regress-ID: cad919522b3ce90c147c95abaf81b0492ac296c9
This commit is contained in:
djm@openbsd.org 2021-02-26 04:18:42 +00:00 committed by Damien Miller
parent 24a3a67bd7
commit 1fe4d70df9
5 changed files with 1 additions and 603 deletions

View File

@ -289,8 +289,6 @@ clean: regressclean
rm -f regress/unittests/match/test_match$(EXEEXT)
rm -f regress/unittests/utf8/*.o
rm -f regress/unittests/utf8/test_utf8$(EXEEXT)
rm -f regress/misc/kexfuzz/*.o
rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT)
rm -f regress/misc/sk-dummy/*.o
rm -f regress/misc/sk-dummy/*.lo
rm -f regress/misc/sk-dummy/sk-dummy.so
@ -324,8 +322,6 @@ distclean: regressclean
rm -f regress/unittests/match/test_match
rm -f regress/unittests/utf8/*.o
rm -f regress/unittests/utf8/test_utf8
rm -f regress/misc/kexfuzz/*.o
rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT)
(cd openbsd-compat && $(MAKE) distclean)
if test -d pkg ; then \
rm -fr pkg ; \
@ -497,7 +493,6 @@ regress-prep:
$(MKDIR_P) `pwd`/regress/unittests/kex
$(MKDIR_P) `pwd`/regress/unittests/match
$(MKDIR_P) `pwd`/regress/unittests/utf8
$(MKDIR_P) `pwd`/regress/misc/kexfuzz
$(MKDIR_P) `pwd`/regress/misc/sk-dummy
[ -f `pwd`/regress/Makefile ] || \
ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile
@ -635,14 +630,6 @@ regress/unittests/utf8/test_utf8$(EXEEXT): \
regress/unittests/test_helper/libtest_helper.a \
-lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS)
MISC_KEX_FUZZ_OBJS=\
regress/misc/kexfuzz/kexfuzz.o \
$(SKOBJS)
regress/misc/kexfuzz/kexfuzz$(EXEEXT): ${MISC_KEX_FUZZ_OBJS} libssh.a
$(LD) -o $@ $(LDFLAGS) $(MISC_KEX_FUZZ_OBJS) \
-lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS)
# These all need to be compiled -fPIC, so they are treated differently.
SK_DUMMY_OBJS=\
regress/misc/sk-dummy/sk-dummy.lo \
@ -676,7 +663,6 @@ regress-unit-binaries: regress-prep $(REGRESSLIBS) \
regress/unittests/kex/test_kex$(EXEEXT) \
regress/unittests/match/test_match$(EXEEXT) \
regress/unittests/utf8/test_utf8$(EXEEXT) \
regress/misc/kexfuzz/kexfuzz$(EXEEXT)
tests: file-tests t-exec interop-tests unit
echo all tests passed

View File

@ -1,3 +1,3 @@
SUBDIR= kexfuzz sk-dummy
SUBDIR= sk-dummy
.include <bsd.subdir.mk>

View File

@ -1,101 +0,0 @@
# $OpenBSD: Makefile,v 1.10 2021/01/09 12:31:46 dtucker Exp $
.include <bsd.own.mk>
.include <bsd.obj.mk>
# XXX detect from ssh binary?
SSH1?= no
OPENSSL?= yes
PROG= kexfuzz
SRCS= kexfuzz.c
SSHREL=../../../../../usr.bin/ssh
.PATH: ${.CURDIR}/${SSHREL}
# From usr.bin/ssh
SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c
SRCS+=atomicio.c sshkey.c authfile.c cipher.c log.c ssh-rsa.c ssh-dss.c
SRCS+=ssh-ecdsa.c ssh-ed25519.c mac.c umac.c umac128.c hmac.c misc.c
SRCS+=ssherr.c uidswap.c cleanup.c xmalloc.c match.c krl.c fatal.c
SRCS+=addr.c addrmatch.c bitmap.c packet.c dispatch.c canohost.c ssh_api.c
SRCS+=compat.c ed25519.c hash.c ge25519.c fe25519.c sc25519.c verify.c
SRCS+=cipher-chachapoly.c chacha.c poly1305.c utf8.c
SRCS+=sshbuf-io.c ssh-ecdsa-sk.c ssh-ed25519-sk.c msg.c ssh-sk-client.c
SRCS+= kex.c
SRCS+= dh.c
SRCS+= kexdh.c
SRCS+= kexecdh.c
SRCS+= kexgex.c
SRCS+= kexgexc.c
SRCS+= kexgexs.c
SRCS+= kexc25519.c
SRCS+= smult_curve25519_ref.c
SRCS+= kexgen.c
SRCS+= kexsntrup761x25519.c
SRCS+= sntrup761.c
SRCS+=digest-openssl.c
#SRCS+=digest-libc.c
NOMAN= 1
.if (${OPENSSL:L} == "yes")
CFLAGS+= -DWITH_OPENSSL
.else
# SSH v.1 requires OpenSSL.
SSH1= no
.endif
.if (${SSH1:L} == "yes")
CFLAGS+= -DWITH_SSH1
.endif
LDADD+= -lfido2 -lcbor -lusbhid
DPADD+= ${LIBFIDO2} ${LIBCBOR} ${LIBUSBHID}
# enable warnings
WARNINGS=Yes
DEBUG=-g
CFLAGS+= -fstack-protector-all
CDIAGFLAGS= -Wall
CDIAGFLAGS+= -Wextra
CDIAGFLAGS+= -Werror
CDIAGFLAGS+= -Wchar-subscripts
CDIAGFLAGS+= -Wcomment
CDIAGFLAGS+= -Wformat
CDIAGFLAGS+= -Wformat-security
CDIAGFLAGS+= -Wimplicit
CDIAGFLAGS+= -Winline
CDIAGFLAGS+= -Wmissing-declarations
CDIAGFLAGS+= -Wmissing-prototypes
CDIAGFLAGS+= -Wparentheses
CDIAGFLAGS+= -Wpointer-arith
CDIAGFLAGS+= -Wreturn-type
CDIAGFLAGS+= -Wshadow
CDIAGFLAGS+= -Wsign-compare
CDIAGFLAGS+= -Wstrict-aliasing
CDIAGFLAGS+= -Wstrict-prototypes
CDIAGFLAGS+= -Wswitch
CDIAGFLAGS+= -Wtrigraphs
CDIAGFLAGS+= -Wuninitialized
CDIAGFLAGS+= -Wunused
CDIAGFLAGS+= -Wno-unused-parameter
.if ${COMPILER_VERSION:L} != "gcc3"
CDIAGFLAGS+= -Wold-style-definition
.endif
CFLAGS+=-I${.CURDIR}/${SSHREL}
LDADD+= -lutil -lz
DPADD+= ${LIBUTIL} ${LIBZ}
.if (${OPENSSL:L} == "yes")
LDADD+= -lcrypto
DPADD+= ${LIBCRYPTO}
.endif
.include <bsd.prog.mk>

View File

@ -1,34 +0,0 @@
This is a harness to help with fuzzing KEX.
To use it, you first set it to count packets in each direction:
./kexfuzz -K diffie-hellman-group1-sha1 -k host_ed25519_key -c
S2C: 29
C2S: 31
Then get it to record a particular packet (in this case the 4th
packet from client->server):
./kexfuzz -K diffie-hellman-group1-sha1 -k host_ed25519_key \
-d -D C2S -i 3 -f packet_3
Fuzz the packet somehow:
dd if=/dev/urandom of=packet_3 bs=32 count=1 # Just for example
Then re-run the key exchange substituting the modified packet in
its original sequence:
./kexfuzz -K diffie-hellman-group1-sha1 -k host_ed25519_key \
-r -D C2S -i 3 -f packet_3
A comprehensive KEX fuzz run would fuzz every packet in both
directions for each key exchange type and every hostkey type.
This will take some time.
Limitations: kexfuzz can't change the ordering of packets at
present. It is limited to replacing individual packets with
fuzzed variants with the same type. It really should allow
insertion, deletion on replacement of packets too.
$OpenBSD: README,v 1.3 2017/10/20 02:13:41 djm Exp $

View File

@ -1,453 +0,0 @@
/* $OpenBSD: kexfuzz.c,v 1.6 2020/01/26 00:09:50 djm Exp $ */
/*
* Fuzz harness for KEX code
*
* Placed in the public domain
*/
#include "includes.h"
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#ifdef HAVE_ERR_H
# include <err.h>
#endif
#include "ssherr.h"
#include "ssh_api.h"
#include "sshbuf.h"
#include "packet.h"
#include "myproposal.h"
#include "authfile.h"
#include "log.h"
void kex_tests(void);
static int do_debug = 0;
enum direction { S2C, C2S };
struct hook_ctx {
struct ssh *client, *server, *server2;
int *c2s, *s2c;
int trigger_direction, packet_index;
const char *dump_path;
struct sshbuf *replace_data;
};
static int
packet_hook(struct ssh *ssh, struct sshbuf *packet, u_char *typep, void *_ctx)
{
struct hook_ctx *ctx = (struct hook_ctx *)_ctx;
int mydirection = ssh == ctx->client ? S2C : C2S;
int *packet_count = mydirection == S2C ? ctx->s2c : ctx->c2s;
FILE *dumpfile;
int r;
if (do_debug) {
printf("%s packet %d type %u:\n",
mydirection == S2C ? "s2c" : "c2s",
*packet_count, *typep);
sshbuf_dump(packet, stdout);
}
if (mydirection == ctx->trigger_direction &&
ctx->packet_index == *packet_count) {
if (ctx->replace_data != NULL) {
sshbuf_reset(packet);
/* Type is first byte of packet */
if ((r = sshbuf_get_u8(ctx->replace_data,
typep)) != 0 ||
(r = sshbuf_putb(packet, ctx->replace_data)) != 0)
return r;
if (do_debug) {
printf("***** replaced packet type %u\n",
*typep);
sshbuf_dump(packet, stdout);
}
} else if (ctx->dump_path != NULL) {
if ((dumpfile = fopen(ctx->dump_path, "w+")) == NULL)
err(1, "fopen %s", ctx->dump_path);
/* Write { type, packet } */
if (fwrite(typep, 1, 1, dumpfile) != 1)
err(1, "fwrite type %s", ctx->dump_path);
if (sshbuf_len(packet) != 0 &&
fwrite(sshbuf_ptr(packet), sshbuf_len(packet),
1, dumpfile) != 1)
err(1, "fwrite body %s", ctx->dump_path);
if (do_debug) {
printf("***** dumped packet type %u len %zu\n",
*typep, sshbuf_len(packet));
}
fclose(dumpfile);
/* No point in continuing */
exit(0);
}
}
(*packet_count)++;
return 0;
}
static int
do_send_and_receive(struct ssh *from, struct ssh *to)
{
u_char type;
size_t len;
const u_char *buf;
int r;
for (;;) {
if ((r = ssh_packet_next(from, &type)) != 0) {
fprintf(stderr, "ssh_packet_next: %s\n", ssh_err(r));
return r;
}
if (type != 0)
return 0;
buf = ssh_output_ptr(from, &len);
if (len == 0)
return 0;
if ((r = ssh_input_append(to, buf, len)) != 0) {
debug("ssh_input_append: %s", ssh_err(r));
return r;
}
if ((r = ssh_output_consume(from, len)) != 0) {
debug("ssh_output_consume: %s", ssh_err(r));
return r;
}
}
}
/* Minimal test_helper.c scaffholding to make this standalone */
const char *in_test = NULL;
#define TEST_START(a) \
do { \
in_test = (a); \
if (do_debug) \
fprintf(stderr, "test %s starting\n", in_test); \
} while (0)
#define TEST_DONE() \
do { \
if (do_debug) \
fprintf(stderr, "test %s done\n", \
in_test ? in_test : "???"); \
in_test = NULL; \
} while(0)
#define ASSERT_INT_EQ(a, b) \
do { \
if ((int)(a) != (int)(b)) { \
fprintf(stderr, "%s %s:%d " \
"%s (%d) != expected %s (%d)\n", \
in_test ? in_test : "(none)", \
__func__, __LINE__, #a, (int)(a), #b, (int)(b)); \
exit(2); \
} \
} while (0)
#define ASSERT_INT_GE(a, b) \
do { \
if ((int)(a) < (int)(b)) { \
fprintf(stderr, "%s %s:%d " \
"%s (%d) < expected %s (%d)\n", \
in_test ? in_test : "(none)", \
__func__, __LINE__, #a, (int)(a), #b, (int)(b)); \
exit(2); \
} \
} while (0)
#define ASSERT_PTR_NE(a, b) \
do { \
if ((a) == (b)) { \
fprintf(stderr, "%s %s:%d " \
"%s (%p) != expected %s (%p)\n", \
in_test ? in_test : "(none)", \
__func__, __LINE__, #a, (a), #b, (b)); \
exit(2); \
} \
} while (0)
static void
run_kex(struct ssh *client, struct ssh *server)
{
int r = 0;
while (!server->kex->done || !client->kex->done) {
if ((r = do_send_and_receive(server, client)) != 0) {
debug("do_send_and_receive S2C: %s", ssh_err(r));
break;
}
if ((r = do_send_and_receive(client, server)) != 0) {
debug("do_send_and_receive C2S: %s", ssh_err(r));
break;
}
}
if (do_debug)
printf("done: %s\n", ssh_err(r));
ASSERT_INT_EQ(r, 0);
ASSERT_INT_EQ(server->kex->done, 1);
ASSERT_INT_EQ(client->kex->done, 1);
}
static void
do_kex_with_key(const char *kex, struct sshkey *prvkey, int *c2s, int *s2c,
int direction, int packet_index,
const char *dump_path, struct sshbuf *replace_data)
{
struct ssh *client = NULL, *server = NULL, *server2 = NULL;
struct sshkey *pubkey = NULL;
struct sshbuf *state;
struct kex_params kex_params;
char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT };
char *keyname = NULL;
struct hook_ctx hook_ctx;
TEST_START("sshkey_from_private");
ASSERT_INT_EQ(sshkey_from_private(prvkey, &pubkey), 0);
TEST_DONE();
TEST_START("ssh_init");
memcpy(kex_params.proposal, myproposal, sizeof(myproposal));
if (kex != NULL)
kex_params.proposal[PROPOSAL_KEX_ALGS] = strdup(kex);
keyname = strdup(sshkey_ssh_name(prvkey));
ASSERT_PTR_NE(keyname, NULL);
kex_params.proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = keyname;
ASSERT_INT_EQ(ssh_init(&client, 0, &kex_params), 0);
ASSERT_INT_EQ(ssh_init(&server, 1, &kex_params), 0);
ASSERT_INT_EQ(ssh_init(&server2, 1, NULL), 0);
ASSERT_PTR_NE(client, NULL);
ASSERT_PTR_NE(server, NULL);
ASSERT_PTR_NE(server2, NULL);
TEST_DONE();
hook_ctx.c2s = c2s;
hook_ctx.s2c = s2c;
hook_ctx.trigger_direction = direction;
hook_ctx.packet_index = packet_index;
hook_ctx.dump_path = dump_path;
hook_ctx.replace_data = replace_data;
hook_ctx.client = client;
hook_ctx.server = server;
hook_ctx.server2 = server2;
ssh_packet_set_input_hook(client, packet_hook, &hook_ctx);
ssh_packet_set_input_hook(server, packet_hook, &hook_ctx);
ssh_packet_set_input_hook(server2, packet_hook, &hook_ctx);
TEST_START("ssh_add_hostkey");
ASSERT_INT_EQ(ssh_add_hostkey(server, prvkey), 0);
ASSERT_INT_EQ(ssh_add_hostkey(client, pubkey), 0);
TEST_DONE();
TEST_START("kex");
run_kex(client, server);
TEST_DONE();
TEST_START("rekeying client");
ASSERT_INT_EQ(kex_send_kexinit(client), 0);
run_kex(client, server);
TEST_DONE();
TEST_START("rekeying server");
ASSERT_INT_EQ(kex_send_kexinit(server), 0);
run_kex(client, server);
TEST_DONE();
TEST_START("ssh_packet_get_state");
state = sshbuf_new();
ASSERT_PTR_NE(state, NULL);
ASSERT_INT_EQ(ssh_packet_get_state(server, state), 0);
ASSERT_INT_GE(sshbuf_len(state), 1);
TEST_DONE();
TEST_START("ssh_packet_set_state");
ASSERT_INT_EQ(ssh_add_hostkey(server2, prvkey), 0);
kex_free(server2->kex); /* XXX or should ssh_packet_set_state()? */
ASSERT_INT_EQ(ssh_packet_set_state(server2, state), 0);
ASSERT_INT_EQ(sshbuf_len(state), 0);
sshbuf_free(state);
ASSERT_PTR_NE(server2->kex, NULL);
/* XXX we need to set the callbacks */
#ifdef WITH_OPENSSL
server2->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_server;
server2->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_server;
server2->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_server;
server2->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_server;
server2->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_server;
server2->kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
server2->kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
# ifdef OPENSSL_HAS_ECC
server2->kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
# endif
#endif
server2->kex->kex[KEX_C25519_SHA256] = kex_gen_server;
server2->kex->load_host_public_key = server->kex->load_host_public_key;
server2->kex->load_host_private_key = server->kex->load_host_private_key;
server2->kex->sign = server->kex->sign;
TEST_DONE();
TEST_START("rekeying server2");
ASSERT_INT_EQ(kex_send_kexinit(server2), 0);
run_kex(client, server2);
ASSERT_INT_EQ(kex_send_kexinit(client), 0);
run_kex(client, server2);
TEST_DONE();
TEST_START("cleanup");
sshkey_free(pubkey);
ssh_free(client);
ssh_free(server);
ssh_free(server2);
free(keyname);
TEST_DONE();
}
static void
usage(void)
{
fprintf(stderr,
"Usage: kexfuzz [-hcdrv] [-D direction] [-f data_file]\n"
" [-K kex_alg] [-k private_key] [-i packet_index]\n"
"\n"
"Options:\n"
" -h Display this help\n"
" -c Count packets sent during KEX\n"
" -d Dump mode: record KEX packet to data file\n"
" -r Replace mode: replace packet with data file\n"
" -v Turn on verbose logging\n"
" -D S2C|C2S Packet direction for replacement or dump\n"
" -f data_file Path to data file for replacement or dump\n"
" -K kex_alg Name of KEX algorithm to test (see below)\n"
" -k private_key Path to private key file\n"
" -i packet_index Index of packet to replace or dump (from 0)\n"
"\n"
"Available KEX algorithms: %s\n", kex_alg_list(' '));
}
static void
badusage(const char *bad)
{
fprintf(stderr, "Invalid options\n");
fprintf(stderr, "%s\n", bad);
usage();
exit(1);
}
int
main(int argc, char **argv)
{
int ch, fd, r;
int count_flag = 0, dump_flag = 0, replace_flag = 0;
int packet_index = -1, direction = -1;
int s2c = 0, c2s = 0; /* packet counts */
const char *kex = NULL, *kpath = NULL, *data_path = NULL;
struct sshkey *key = NULL;
struct sshbuf *replace_data = NULL;
setvbuf(stdout, NULL, _IONBF, 0);
while ((ch = getopt(argc, argv, "hcdrvD:f:K:k:i:")) != -1) {
switch (ch) {
case 'h':
usage();
return 0;
case 'c':
count_flag = 1;
break;
case 'd':
dump_flag = 1;
break;
case 'r':
replace_flag = 1;
break;
case 'v':
do_debug = 1;
break;
case 'D':
if (strcasecmp(optarg, "s2c") == 0)
direction = S2C;
else if (strcasecmp(optarg, "c2s") == 0)
direction = C2S;
else
badusage("Invalid direction (-D)");
break;
case 'f':
data_path = optarg;
break;
case 'K':
kex = optarg;
break;
case 'k':
kpath = optarg;
break;
case 'i':
packet_index = atoi(optarg);
if (packet_index < 0)
badusage("Invalid packet index");
break;
default:
badusage("unsupported flag");
}
}
argc -= optind;
argv += optind;
log_init(argv[0], do_debug ? SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_INFO,
SYSLOG_FACILITY_USER, 1);
/* Must select a single mode */
if ((count_flag + dump_flag + replace_flag) != 1)
badusage("Must select one mode: -c, -d or -r");
/* KEX type is mandatory */
if (kex == NULL || !kex_names_valid(kex) || strchr(kex, ',') != NULL)
badusage("Missing or invalid kex type (-K flag)");
/* Valid key is mandatory */
if (kpath == NULL)
badusage("Missing private key (-k flag)");
if ((fd = open(kpath, O_RDONLY)) == -1)
err(1, "open %s", kpath);
if ((r = sshkey_load_private_type_fd(fd, KEY_UNSPEC, NULL,
&key, NULL)) != 0)
errx(1, "Unable to load key %s: %s", kpath, ssh_err(r));
close(fd);
/* XXX check that it is a private key */
/* XXX support certificates */
if (key == NULL || key->type == KEY_UNSPEC)
badusage("Invalid key file (-k flag)");
/* Replace (fuzz) mode */
if (replace_flag) {
if (packet_index == -1 || direction == -1 || data_path == NULL)
badusage("Replace (-r) mode must specify direction "
"(-D) packet index (-i) and data path (-f)");
if ((r = sshbuf_load_file(data_path, &replace_data)) != 0)
errx(1, "read %s: %s", data_path, ssh_err(r));
}
/* Dump mode */
if (dump_flag) {
if (packet_index == -1 || direction == -1 || data_path == NULL)
badusage("Dump (-d) mode must specify direction "
"(-D), packet index (-i) and data path (-f)");
}
/* Count mode needs no further flags */
do_kex_with_key(kex, key, &c2s, &s2c,
direction, packet_index,
dump_flag ? data_path : NULL,
replace_flag ? replace_data : NULL);
sshkey_free(key);
sshbuf_free(replace_data);
if (count_flag) {
printf("S2C: %d\n", s2c);
printf("C2S: %d\n", c2s);
}
return 0;
}