mirror of
git://anongit.mindrot.org/openssh.git
synced 2024-11-23 18:23:25 +08:00
upstream commit
fix the KEX fuzzer - the previous method of obtaining the packet contents was broken. This now uses the new per-packet input hook, so it sees exact post-decrypt packets and doesn't have to pass packet integrity checks. ok markus@ Upstream-Regress-ID: 402fb6ffabd97de590e8e57b25788949dce8d2fd
This commit is contained in:
parent
09f997893f
commit
1723ec92eb
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: kexfuzz.c,v 1.2 2016/09/16 01:01:41 djm Exp $ */
|
||||
/* $OpenBSD: kexfuzz.c,v 1.3 2016/10/11 21:49:54 djm Exp $ */
|
||||
/*
|
||||
* Fuzz harness for KEX code
|
||||
*
|
||||
@ -27,6 +27,7 @@
|
||||
#include "packet.h"
|
||||
#include "myproposal.h"
|
||||
#include "authfile.h"
|
||||
#include "log.h"
|
||||
|
||||
struct ssh *active_state = NULL; /* XXX - needed for linking */
|
||||
|
||||
@ -35,61 +36,93 @@ 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
|
||||
do_send_and_receive(struct ssh *from, struct ssh *to, int mydirection,
|
||||
int *packet_count, int trigger_direction, int packet_index,
|
||||
const char *dump_path, struct sshbuf *replace_data)
|
||||
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, olen;
|
||||
size_t len;
|
||||
const u_char *buf;
|
||||
int r;
|
||||
FILE *dumpfile;
|
||||
|
||||
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);
|
||||
olen = len;
|
||||
if (do_debug) {
|
||||
printf("%s packet %d type %u len %zu:\n",
|
||||
mydirection == S2C ? "s2c" : "c2s",
|
||||
*packet_count, type, len);
|
||||
sshbuf_dump_data(buf, len, stdout);
|
||||
}
|
||||
if (mydirection == trigger_direction &&
|
||||
packet_index == *packet_count) {
|
||||
if (replace_data != NULL) {
|
||||
buf = sshbuf_ptr(replace_data);
|
||||
len = sshbuf_len(replace_data);
|
||||
if (do_debug) {
|
||||
printf("***** replaced packet "
|
||||
"len %zu\n", len);
|
||||
sshbuf_dump_data(buf, len, stdout);
|
||||
}
|
||||
} else if (dump_path != NULL) {
|
||||
if ((dumpfile = fopen(dump_path, "w+")) == NULL)
|
||||
err(1, "fopen %s", dump_path);
|
||||
if (len != 0 &&
|
||||
fwrite(buf, len, 1, dumpfile) != 1)
|
||||
err(1, "fwrite %s", dump_path);
|
||||
if (do_debug)
|
||||
printf("***** dumped packet "
|
||||
"len %zu\n", len);
|
||||
fclose(dumpfile);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
(*packet_count)++;
|
||||
if (len == 0)
|
||||
return 0;
|
||||
if ((r = ssh_input_append(to, buf, len)) != 0 ||
|
||||
(r = ssh_output_consume(from, olen)) != 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,19 +174,19 @@ const char *in_test = NULL;
|
||||
|
||||
|
||||
static void
|
||||
run_kex(struct ssh *client, struct ssh *server, int *s2c, int *c2s,
|
||||
int direction, int packet_index,
|
||||
const char *dump_path, struct sshbuf *replace_data)
|
||||
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, S2C, s2c,
|
||||
direction, packet_index, dump_path, replace_data)))
|
||||
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, C2S, c2s,
|
||||
direction, packet_index, dump_path, replace_data)))
|
||||
}
|
||||
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));
|
||||
@ -173,6 +206,7 @@ do_kex_with_key(const char *kex, struct sshkey *prvkey, int *c2s, int *s2c,
|
||||
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);
|
||||
@ -187,30 +221,42 @@ do_kex_with_key(const char *kex, struct sshkey *prvkey, int *c2s, int *s2c,
|
||||
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, s2c, c2s, direction, packet_index,
|
||||
dump_path, replace_data);
|
||||
run_kex(client, server);
|
||||
TEST_DONE();
|
||||
|
||||
TEST_START("rekeying client");
|
||||
ASSERT_INT_EQ(kex_send_kexinit(client), 0);
|
||||
run_kex(client, server, s2c, c2s, direction, packet_index,
|
||||
dump_path, replace_data);
|
||||
run_kex(client, server);
|
||||
TEST_DONE();
|
||||
|
||||
TEST_START("rekeying server");
|
||||
ASSERT_INT_EQ(kex_send_kexinit(server), 0);
|
||||
run_kex(client, server, s2c, c2s, direction, packet_index,
|
||||
dump_path, replace_data);
|
||||
run_kex(client, server);
|
||||
TEST_DONE();
|
||||
|
||||
TEST_START("ssh_packet_get_state");
|
||||
@ -221,9 +267,6 @@ do_kex_with_key(const char *kex, struct sshkey *prvkey, int *c2s, int *s2c,
|
||||
TEST_DONE();
|
||||
|
||||
TEST_START("ssh_packet_set_state");
|
||||
server2 = NULL;
|
||||
ASSERT_INT_EQ(ssh_init(&server2, 1, NULL), 0);
|
||||
ASSERT_PTR_NE(server2, NULL);
|
||||
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);
|
||||
@ -251,11 +294,9 @@ do_kex_with_key(const char *kex, struct sshkey *prvkey, int *c2s, int *s2c,
|
||||
|
||||
TEST_START("rekeying server2");
|
||||
ASSERT_INT_EQ(kex_send_kexinit(server2), 0);
|
||||
run_kex(client, server2, s2c, c2s, direction, packet_index,
|
||||
dump_path, replace_data);
|
||||
run_kex(client, server2);
|
||||
ASSERT_INT_EQ(kex_send_kexinit(client), 0);
|
||||
run_kex(client, server2, s2c, c2s, direction, packet_index,
|
||||
dump_path, replace_data);
|
||||
run_kex(client, server2);
|
||||
TEST_DONE();
|
||||
|
||||
TEST_START("cleanup");
|
||||
@ -357,6 +398,9 @@ main(int argc, char **argv)
|
||||
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");
|
||||
|
Loading…
Reference in New Issue
Block a user