diff options
author | djm@openbsd.org <djm@openbsd.org> | 2016-10-11 21:49:54 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2016-10-13 18:55:25 +1100 |
commit | 1723ec92eb485ce06b4cbf49712d21975d873909 (patch) | |
tree | ec8660501fca7aae241f434e2c3c6e4bcb6e1406 | |
parent | 09f997893f109799cddbfce6d7e67f787045cbb2 (diff) |
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
-rw-r--r-- | regress/misc/kexfuzz/kexfuzz.c | 162 |
1 files changed, 103 insertions, 59 deletions
diff --git a/regress/misc/kexfuzz/kexfuzz.c b/regress/misc/kexfuzz/kexfuzz.c index 8535980b0..67058027f 100644 --- a/regress/misc/kexfuzz/kexfuzz.c +++ b/regress/misc/kexfuzz/kexfuzz.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: kexfuzz.c,v 1.2 2016/09/16 01:01:41 djm Exp $ */ | 1 | /* $OpenBSD: kexfuzz.c,v 1.3 2016/10/11 21:49:54 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Fuzz harness for KEX code | 3 | * Fuzz harness for KEX code |
4 | * | 4 | * |
@@ -27,6 +27,7 @@ | |||
27 | #include "packet.h" | 27 | #include "packet.h" |
28 | #include "myproposal.h" | 28 | #include "myproposal.h" |
29 | #include "authfile.h" | 29 | #include "authfile.h" |
30 | #include "log.h" | ||
30 | 31 | ||
31 | struct ssh *active_state = NULL; /* XXX - needed for linking */ | 32 | struct ssh *active_state = NULL; /* XXX - needed for linking */ |
32 | 33 | ||
@@ -35,61 +36,93 @@ static int do_debug = 0; | |||
35 | 36 | ||
36 | enum direction { S2C, C2S }; | 37 | enum direction { S2C, C2S }; |
37 | 38 | ||
39 | struct hook_ctx { | ||
40 | struct ssh *client, *server, *server2; | ||
41 | int *c2s, *s2c; | ||
42 | int trigger_direction, packet_index; | ||
43 | const char *dump_path; | ||
44 | struct sshbuf *replace_data; | ||
45 | }; | ||
46 | |||
38 | static int | 47 | static int |
39 | do_send_and_receive(struct ssh *from, struct ssh *to, int mydirection, | 48 | packet_hook(struct ssh *ssh, struct sshbuf *packet, u_char *typep, void *_ctx) |
40 | int *packet_count, int trigger_direction, int packet_index, | 49 | { |
41 | const char *dump_path, struct sshbuf *replace_data) | 50 | struct hook_ctx *ctx = (struct hook_ctx *)_ctx; |
51 | int mydirection = ssh == ctx->client ? S2C : C2S; | ||
52 | int *packet_count = mydirection == S2C ? ctx->s2c : ctx->c2s; | ||
53 | FILE *dumpfile; | ||
54 | int r; | ||
55 | |||
56 | if (do_debug) { | ||
57 | printf("%s packet %d type %u:\n", | ||
58 | mydirection == S2C ? "s2c" : "c2s", | ||
59 | *packet_count, *typep); | ||
60 | sshbuf_dump(packet, stdout); | ||
61 | } | ||
62 | if (mydirection == ctx->trigger_direction && | ||
63 | ctx->packet_index == *packet_count) { | ||
64 | if (ctx->replace_data != NULL) { | ||
65 | sshbuf_reset(packet); | ||
66 | /* Type is first byte of packet */ | ||
67 | if ((r = sshbuf_get_u8(ctx->replace_data, | ||
68 | typep)) != 0 || | ||
69 | (r = sshbuf_putb(packet, ctx->replace_data)) != 0) | ||
70 | return r; | ||
71 | if (do_debug) { | ||
72 | printf("***** replaced packet type %u\n", | ||
73 | *typep); | ||
74 | sshbuf_dump(packet, stdout); | ||
75 | } | ||
76 | } else if (ctx->dump_path != NULL) { | ||
77 | if ((dumpfile = fopen(ctx->dump_path, "w+")) == NULL) | ||
78 | err(1, "fopen %s", ctx->dump_path); | ||
79 | /* Write { type, packet } */ | ||
80 | if (fwrite(typep, 1, 1, dumpfile) != 1) | ||
81 | err(1, "fwrite type %s", ctx->dump_path); | ||
82 | if (sshbuf_len(packet) != 0 && | ||
83 | fwrite(sshbuf_ptr(packet), sshbuf_len(packet), | ||
84 | 1, dumpfile) != 1) | ||
85 | err(1, "fwrite body %s", ctx->dump_path); | ||
86 | if (do_debug) { | ||
87 | printf("***** dumped packet type %u len %zu\n", | ||
88 | *typep, sshbuf_len(packet)); | ||
89 | } | ||
90 | fclose(dumpfile); | ||
91 | /* No point in continuing */ | ||
92 | exit(0); | ||
93 | } | ||
94 | } | ||
95 | (*packet_count)++; | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static int | ||
100 | do_send_and_receive(struct ssh *from, struct ssh *to) | ||
42 | { | 101 | { |
43 | u_char type; | 102 | u_char type; |
44 | size_t len, olen; | 103 | size_t len; |
45 | const u_char *buf; | 104 | const u_char *buf; |
46 | int r; | 105 | int r; |
47 | FILE *dumpfile; | ||
48 | 106 | ||
49 | for (;;) { | 107 | for (;;) { |
50 | if ((r = ssh_packet_next(from, &type)) != 0) { | 108 | if ((r = ssh_packet_next(from, &type)) != 0) { |
51 | fprintf(stderr, "ssh_packet_next: %s\n", ssh_err(r)); | 109 | fprintf(stderr, "ssh_packet_next: %s\n", ssh_err(r)); |
52 | return r; | 110 | return r; |
53 | } | 111 | } |
112 | |||
54 | if (type != 0) | 113 | if (type != 0) |
55 | return 0; | 114 | return 0; |
56 | buf = ssh_output_ptr(from, &len); | 115 | buf = ssh_output_ptr(from, &len); |
57 | olen = len; | ||
58 | if (do_debug) { | ||
59 | printf("%s packet %d type %u len %zu:\n", | ||
60 | mydirection == S2C ? "s2c" : "c2s", | ||
61 | *packet_count, type, len); | ||
62 | sshbuf_dump_data(buf, len, stdout); | ||
63 | } | ||
64 | if (mydirection == trigger_direction && | ||
65 | packet_index == *packet_count) { | ||
66 | if (replace_data != NULL) { | ||
67 | buf = sshbuf_ptr(replace_data); | ||
68 | len = sshbuf_len(replace_data); | ||
69 | if (do_debug) { | ||
70 | printf("***** replaced packet " | ||
71 | "len %zu\n", len); | ||
72 | sshbuf_dump_data(buf, len, stdout); | ||
73 | } | ||
74 | } else if (dump_path != NULL) { | ||
75 | if ((dumpfile = fopen(dump_path, "w+")) == NULL) | ||
76 | err(1, "fopen %s", dump_path); | ||
77 | if (len != 0 && | ||
78 | fwrite(buf, len, 1, dumpfile) != 1) | ||
79 | err(1, "fwrite %s", dump_path); | ||
80 | if (do_debug) | ||
81 | printf("***** dumped packet " | ||
82 | "len %zu\n", len); | ||
83 | fclose(dumpfile); | ||
84 | exit(0); | ||
85 | } | ||
86 | } | ||
87 | (*packet_count)++; | ||
88 | if (len == 0) | 116 | if (len == 0) |
89 | return 0; | 117 | return 0; |
90 | if ((r = ssh_input_append(to, buf, len)) != 0 || | 118 | if ((r = ssh_input_append(to, buf, len)) != 0) { |
91 | (r = ssh_output_consume(from, olen)) != 0) | 119 | debug("ssh_input_append: %s", ssh_err(r)); |
120 | return r; | ||
121 | } | ||
122 | if ((r = ssh_output_consume(from, len)) != 0) { | ||
123 | debug("ssh_output_consume: %s", ssh_err(r)); | ||
92 | return r; | 124 | return r; |
125 | } | ||
93 | } | 126 | } |
94 | } | 127 | } |
95 | 128 | ||
@@ -141,19 +174,19 @@ const char *in_test = NULL; | |||
141 | 174 | ||
142 | 175 | ||
143 | static void | 176 | static void |
144 | run_kex(struct ssh *client, struct ssh *server, int *s2c, int *c2s, | 177 | run_kex(struct ssh *client, struct ssh *server) |
145 | int direction, int packet_index, | ||
146 | const char *dump_path, struct sshbuf *replace_data) | ||
147 | { | 178 | { |
148 | int r = 0; | 179 | int r = 0; |
149 | 180 | ||
150 | while (!server->kex->done || !client->kex->done) { | 181 | while (!server->kex->done || !client->kex->done) { |
151 | if ((r = do_send_and_receive(server, client, S2C, s2c, | 182 | if ((r = do_send_and_receive(server, client)) != 0) { |
152 | direction, packet_index, dump_path, replace_data))) | 183 | debug("do_send_and_receive S2C: %s", ssh_err(r)); |
153 | break; | 184 | break; |
154 | if ((r = do_send_and_receive(client, server, C2S, c2s, | 185 | } |
155 | direction, packet_index, dump_path, replace_data))) | 186 | if ((r = do_send_and_receive(client, server)) != 0) { |
187 | debug("do_send_and_receive C2S: %s", ssh_err(r)); | ||
156 | break; | 188 | break; |
189 | } | ||
157 | } | 190 | } |
158 | if (do_debug) | 191 | if (do_debug) |
159 | printf("done: %s\n", ssh_err(r)); | 192 | 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, | |||
173 | struct kex_params kex_params; | 206 | struct kex_params kex_params; |
174 | char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; | 207 | char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; |
175 | char *keyname = NULL; | 208 | char *keyname = NULL; |
209 | struct hook_ctx hook_ctx; | ||
176 | 210 | ||
177 | TEST_START("sshkey_from_private"); | 211 | TEST_START("sshkey_from_private"); |
178 | ASSERT_INT_EQ(sshkey_from_private(prvkey, &pubkey), 0); | 212 | 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, | |||
187 | kex_params.proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = keyname; | 221 | kex_params.proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = keyname; |
188 | ASSERT_INT_EQ(ssh_init(&client, 0, &kex_params), 0); | 222 | ASSERT_INT_EQ(ssh_init(&client, 0, &kex_params), 0); |
189 | ASSERT_INT_EQ(ssh_init(&server, 1, &kex_params), 0); | 223 | ASSERT_INT_EQ(ssh_init(&server, 1, &kex_params), 0); |
224 | ASSERT_INT_EQ(ssh_init(&server2, 1, NULL), 0); | ||
190 | ASSERT_PTR_NE(client, NULL); | 225 | ASSERT_PTR_NE(client, NULL); |
191 | ASSERT_PTR_NE(server, NULL); | 226 | ASSERT_PTR_NE(server, NULL); |
227 | ASSERT_PTR_NE(server2, NULL); | ||
192 | TEST_DONE(); | 228 | TEST_DONE(); |
193 | 229 | ||
230 | hook_ctx.c2s = c2s; | ||
231 | hook_ctx.s2c = s2c; | ||
232 | hook_ctx.trigger_direction = direction; | ||
233 | hook_ctx.packet_index = packet_index; | ||
234 | hook_ctx.dump_path = dump_path; | ||
235 | hook_ctx.replace_data = replace_data; | ||
236 | hook_ctx.client = client; | ||
237 | hook_ctx.server = server; | ||
238 | hook_ctx.server2 = server2; | ||
239 | ssh_packet_set_input_hook(client, packet_hook, &hook_ctx); | ||
240 | ssh_packet_set_input_hook(server, packet_hook, &hook_ctx); | ||
241 | ssh_packet_set_input_hook(server2, packet_hook, &hook_ctx); | ||
242 | |||
194 | TEST_START("ssh_add_hostkey"); | 243 | TEST_START("ssh_add_hostkey"); |
195 | ASSERT_INT_EQ(ssh_add_hostkey(server, prvkey), 0); | 244 | ASSERT_INT_EQ(ssh_add_hostkey(server, prvkey), 0); |
196 | ASSERT_INT_EQ(ssh_add_hostkey(client, pubkey), 0); | 245 | ASSERT_INT_EQ(ssh_add_hostkey(client, pubkey), 0); |
197 | TEST_DONE(); | 246 | TEST_DONE(); |
198 | 247 | ||
199 | TEST_START("kex"); | 248 | TEST_START("kex"); |
200 | run_kex(client, server, s2c, c2s, direction, packet_index, | 249 | run_kex(client, server); |
201 | dump_path, replace_data); | ||
202 | TEST_DONE(); | 250 | TEST_DONE(); |
203 | 251 | ||
204 | TEST_START("rekeying client"); | 252 | TEST_START("rekeying client"); |
205 | ASSERT_INT_EQ(kex_send_kexinit(client), 0); | 253 | ASSERT_INT_EQ(kex_send_kexinit(client), 0); |
206 | run_kex(client, server, s2c, c2s, direction, packet_index, | 254 | run_kex(client, server); |
207 | dump_path, replace_data); | ||
208 | TEST_DONE(); | 255 | TEST_DONE(); |
209 | 256 | ||
210 | TEST_START("rekeying server"); | 257 | TEST_START("rekeying server"); |
211 | ASSERT_INT_EQ(kex_send_kexinit(server), 0); | 258 | ASSERT_INT_EQ(kex_send_kexinit(server), 0); |
212 | run_kex(client, server, s2c, c2s, direction, packet_index, | 259 | run_kex(client, server); |
213 | dump_path, replace_data); | ||
214 | TEST_DONE(); | 260 | TEST_DONE(); |
215 | 261 | ||
216 | TEST_START("ssh_packet_get_state"); | 262 | TEST_START("ssh_packet_get_state"); |
@@ -221,9 +267,6 @@ do_kex_with_key(const char *kex, struct sshkey *prvkey, int *c2s, int *s2c, | |||
221 | TEST_DONE(); | 267 | TEST_DONE(); |
222 | 268 | ||
223 | TEST_START("ssh_packet_set_state"); | 269 | TEST_START("ssh_packet_set_state"); |
224 | server2 = NULL; | ||
225 | ASSERT_INT_EQ(ssh_init(&server2, 1, NULL), 0); | ||
226 | ASSERT_PTR_NE(server2, NULL); | ||
227 | ASSERT_INT_EQ(ssh_add_hostkey(server2, prvkey), 0); | 270 | ASSERT_INT_EQ(ssh_add_hostkey(server2, prvkey), 0); |
228 | kex_free(server2->kex); /* XXX or should ssh_packet_set_state()? */ | 271 | kex_free(server2->kex); /* XXX or should ssh_packet_set_state()? */ |
229 | ASSERT_INT_EQ(ssh_packet_set_state(server2, state), 0); | 272 | 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, | |||
251 | 294 | ||
252 | TEST_START("rekeying server2"); | 295 | TEST_START("rekeying server2"); |
253 | ASSERT_INT_EQ(kex_send_kexinit(server2), 0); | 296 | ASSERT_INT_EQ(kex_send_kexinit(server2), 0); |
254 | run_kex(client, server2, s2c, c2s, direction, packet_index, | 297 | run_kex(client, server2); |
255 | dump_path, replace_data); | ||
256 | ASSERT_INT_EQ(kex_send_kexinit(client), 0); | 298 | ASSERT_INT_EQ(kex_send_kexinit(client), 0); |
257 | run_kex(client, server2, s2c, c2s, direction, packet_index, | 299 | run_kex(client, server2); |
258 | dump_path, replace_data); | ||
259 | TEST_DONE(); | 300 | TEST_DONE(); |
260 | 301 | ||
261 | TEST_START("cleanup"); | 302 | TEST_START("cleanup"); |
@@ -357,6 +398,9 @@ main(int argc, char **argv) | |||
357 | argc -= optind; | 398 | argc -= optind; |
358 | argv += optind; | 399 | argv += optind; |
359 | 400 | ||
401 | log_init(argv[0], do_debug ? SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_INFO, | ||
402 | SYSLOG_FACILITY_USER, 1); | ||
403 | |||
360 | /* Must select a single mode */ | 404 | /* Must select a single mode */ |
361 | if ((count_flag + dump_flag + replace_flag) != 1) | 405 | if ((count_flag + dump_flag + replace_flag) != 1) |
362 | badusage("Must select one mode: -c, -d or -r"); | 406 | badusage("Must select one mode: -c, -d or -r"); |