diff options
-rw-r--r-- | regress/Makefile | 2 | ||||
-rw-r--r-- | regress/misc/Makefile | 3 | ||||
-rw-r--r-- | regress/misc/kexfuzz/Makefile | 78 | ||||
-rw-r--r-- | regress/misc/kexfuzz/README | 28 | ||||
-rw-r--r-- | regress/misc/kexfuzz/kexfuzz.c | 404 |
5 files changed, 514 insertions, 1 deletions
diff --git a/regress/Makefile b/regress/Makefile index 451909c1a..839fb8eca 100644 --- a/regress/Makefile +++ b/regress/Makefile | |||
@@ -1,4 +1,4 @@ | |||
1 | # $OpenBSD: Makefile,v 1.82 2015/09/24 06:16:53 djm Exp $ | 1 | # $OpenBSD: Makefile,v 1.84 2016/03/04 02:30:36 djm Exp $ |
2 | 2 | ||
3 | REGRESS_TARGETS= unit t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 t-exec | 3 | REGRESS_TARGETS= unit t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 t-exec |
4 | tests: prep $(REGRESS_TARGETS) | 4 | tests: prep $(REGRESS_TARGETS) |
diff --git a/regress/misc/Makefile b/regress/misc/Makefile new file mode 100644 index 000000000..14c0c279f --- /dev/null +++ b/regress/misc/Makefile | |||
@@ -0,0 +1,3 @@ | |||
1 | SUBDIR= kexfuzz | ||
2 | |||
3 | .include <bsd.subdir.mk> | ||
diff --git a/regress/misc/kexfuzz/Makefile b/regress/misc/kexfuzz/Makefile new file mode 100644 index 000000000..3018b632f --- /dev/null +++ b/regress/misc/kexfuzz/Makefile | |||
@@ -0,0 +1,78 @@ | |||
1 | # $OpenBSD: Makefile,v 1.1 2016/03/04 02:30:37 djm Exp $ | ||
2 | |||
3 | .include <bsd.own.mk> | ||
4 | .include <bsd.obj.mk> | ||
5 | |||
6 | # XXX detect from ssh binary? | ||
7 | SSH1?= no | ||
8 | OPENSSL?= yes | ||
9 | |||
10 | PROG= kexfuzz | ||
11 | SRCS= kexfuzz.c | ||
12 | NOMAN= 1 | ||
13 | |||
14 | .if (${OPENSSL:L} == "yes") | ||
15 | CFLAGS+= -DWITH_OPENSSL | ||
16 | .else | ||
17 | # SSH v.1 requires OpenSSL. | ||
18 | SSH1= no | ||
19 | .endif | ||
20 | |||
21 | .if (${SSH1:L} == "yes") | ||
22 | CFLAGS+= -DWITH_SSH1 | ||
23 | .endif | ||
24 | |||
25 | # enable warnings | ||
26 | WARNINGS=Yes | ||
27 | |||
28 | DEBUG=-g | ||
29 | CFLAGS+= -fstack-protector-all | ||
30 | CDIAGFLAGS= -Wall | ||
31 | CDIAGFLAGS+= -Wextra | ||
32 | CDIAGFLAGS+= -Werror | ||
33 | CDIAGFLAGS+= -Wchar-subscripts | ||
34 | CDIAGFLAGS+= -Wcomment | ||
35 | CDIAGFLAGS+= -Wformat | ||
36 | CDIAGFLAGS+= -Wformat-security | ||
37 | CDIAGFLAGS+= -Wimplicit | ||
38 | CDIAGFLAGS+= -Winline | ||
39 | CDIAGFLAGS+= -Wmissing-declarations | ||
40 | CDIAGFLAGS+= -Wmissing-prototypes | ||
41 | CDIAGFLAGS+= -Wparentheses | ||
42 | CDIAGFLAGS+= -Wpointer-arith | ||
43 | CDIAGFLAGS+= -Wreturn-type | ||
44 | CDIAGFLAGS+= -Wshadow | ||
45 | CDIAGFLAGS+= -Wsign-compare | ||
46 | CDIAGFLAGS+= -Wstrict-aliasing | ||
47 | CDIAGFLAGS+= -Wstrict-prototypes | ||
48 | CDIAGFLAGS+= -Wswitch | ||
49 | CDIAGFLAGS+= -Wtrigraphs | ||
50 | CDIAGFLAGS+= -Wuninitialized | ||
51 | CDIAGFLAGS+= -Wunused | ||
52 | .if ${COMPILER_VERSION} == "gcc4" | ||
53 | CDIAGFLAGS+= -Wpointer-sign | ||
54 | CDIAGFLAGS+= -Wold-style-definition | ||
55 | .endif | ||
56 | |||
57 | SSHREL=../../../../../usr.bin/ssh | ||
58 | |||
59 | CFLAGS+=-I${.CURDIR}/${SSHREL} | ||
60 | |||
61 | .if exists(${.CURDIR}/${SSHREL}/lib/${__objdir}) | ||
62 | LDADD+=-L${.CURDIR}/${SSHREL}/lib/${__objdir} -lssh | ||
63 | DPADD+=${.CURDIR}/${SSHREL}/lib/${__objdir}/libssh.a | ||
64 | .else | ||
65 | LDADD+=-L${.CURDIR}/${SSHREL}/lib -lssh | ||
66 | DPADD+=${.CURDIR}/${SSHREL}/lib/libssh.a | ||
67 | .endif | ||
68 | |||
69 | LDADD+= -lutil -lz | ||
70 | DPADD+= ${LIBUTIL} ${LIBZ} | ||
71 | |||
72 | .if (${OPENSSL:L} == "yes") | ||
73 | LDADD+= -lcrypto | ||
74 | DPADD+= ${LIBCRYPTO} | ||
75 | .endif | ||
76 | |||
77 | .include <bsd.prog.mk> | ||
78 | |||
diff --git a/regress/misc/kexfuzz/README b/regress/misc/kexfuzz/README new file mode 100644 index 000000000..8b215b5bf --- /dev/null +++ b/regress/misc/kexfuzz/README | |||
@@ -0,0 +1,28 @@ | |||
1 | This is a harness to help with fuzzing KEX. | ||
2 | |||
3 | To use it, you first set it to count packets in each direction: | ||
4 | |||
5 | ./kexfuzz -K diffie-hellman-group1-sha1 -k host_ed25519_key -c | ||
6 | S2C: 29 | ||
7 | C2S: 31 | ||
8 | |||
9 | Then get it to record a particular packet (in this case the 4th | ||
10 | packet from client->server): | ||
11 | |||
12 | ./kexfuzz -K diffie-hellman-group1-sha1 -k host_ed25519_key \ | ||
13 | -d -D C2S -i 3 -f packet_3 | ||
14 | |||
15 | Fuzz the packet somehow: | ||
16 | |||
17 | dd if=/dev/urandom of=packet_3 bs=32 count=1 # Just for example | ||
18 | |||
19 | Then re-run the key exchange substituting the modified packet in | ||
20 | its original sequence: | ||
21 | |||
22 | ./kexfuzz -K diffie-hellman-group1-sha1 -k host_ed25519_key \ | ||
23 | -r -D C2S -i 3 -f packet_3 | ||
24 | |||
25 | A comprehensive KEX fuzz run would fuzz every packet in both | ||
26 | directions for each key exchange type and every hostkey type. | ||
27 | This will take some time. | ||
28 | |||
diff --git a/regress/misc/kexfuzz/kexfuzz.c b/regress/misc/kexfuzz/kexfuzz.c new file mode 100644 index 000000000..d57a117ac --- /dev/null +++ b/regress/misc/kexfuzz/kexfuzz.c | |||
@@ -0,0 +1,404 @@ | |||
1 | /* $OpenBSD: kexfuzz.c,v 1.1 2016/03/04 02:30:37 djm Exp $ */ | ||
2 | /* | ||
3 | * Fuzz harness for KEX code | ||
4 | * | ||
5 | * Placed in the public domain | ||
6 | */ | ||
7 | |||
8 | #include <sys/types.h> | ||
9 | #include <sys/param.h> | ||
10 | #include <stdio.h> | ||
11 | #include <stdint.h> | ||
12 | #include <stdlib.h> | ||
13 | #include <string.h> | ||
14 | #include <unistd.h> | ||
15 | #include <fcntl.h> | ||
16 | #include <err.h> | ||
17 | |||
18 | #include "ssherr.h" | ||
19 | #include "ssh_api.h" | ||
20 | #include "sshbuf.h" | ||
21 | #include "packet.h" | ||
22 | #include "myproposal.h" | ||
23 | #include "authfile.h" | ||
24 | |||
25 | struct ssh *active_state = NULL; /* XXX - needed for linking */ | ||
26 | |||
27 | void kex_tests(void); | ||
28 | static int do_debug = 0; | ||
29 | |||
30 | enum direction { S2C, C2S }; | ||
31 | |||
32 | static int | ||
33 | do_send_and_receive(struct ssh *from, struct ssh *to, int mydirection, | ||
34 | int *packet_count, int trigger_direction, int packet_index, | ||
35 | const char *dump_path, struct sshbuf *replace_data) | ||
36 | { | ||
37 | u_char type; | ||
38 | size_t len, olen; | ||
39 | const u_char *buf; | ||
40 | int r; | ||
41 | FILE *dumpfile; | ||
42 | |||
43 | for (;;) { | ||
44 | if ((r = ssh_packet_next(from, &type)) != 0) { | ||
45 | fprintf(stderr, "ssh_packet_next: %s\n", ssh_err(r)); | ||
46 | return r; | ||
47 | } | ||
48 | if (type != 0) | ||
49 | return 0; | ||
50 | buf = ssh_output_ptr(from, &len); | ||
51 | olen = len; | ||
52 | if (do_debug) { | ||
53 | printf("%s packet %d type %u len %zu:\n", | ||
54 | mydirection == S2C ? "s2c" : "c2s", | ||
55 | *packet_count, type, len); | ||
56 | sshbuf_dump_data(buf, len, stdout); | ||
57 | } | ||
58 | if (mydirection == trigger_direction && | ||
59 | packet_index == *packet_count) { | ||
60 | if (replace_data != NULL) { | ||
61 | buf = sshbuf_ptr(replace_data); | ||
62 | len = sshbuf_len(replace_data); | ||
63 | if (do_debug) { | ||
64 | printf("***** replaced packet " | ||
65 | "len %zu\n", len); | ||
66 | sshbuf_dump_data(buf, len, stdout); | ||
67 | } | ||
68 | } else if (dump_path != NULL) { | ||
69 | if ((dumpfile = fopen(dump_path, "w+")) == NULL) | ||
70 | err(1, "fopen %s", dump_path); | ||
71 | if (len != 0 && | ||
72 | fwrite(buf, len, 1, dumpfile) != 1) | ||
73 | err(1, "fwrite %s", dump_path); | ||
74 | if (do_debug) | ||
75 | printf("***** dumped packet " | ||
76 | "len %zu\n", len); | ||
77 | fclose(dumpfile); | ||
78 | exit(0); | ||
79 | } | ||
80 | } | ||
81 | (*packet_count)++; | ||
82 | if (len == 0) | ||
83 | return 0; | ||
84 | if ((r = ssh_input_append(to, buf, len)) != 0 || | ||
85 | (r = ssh_output_consume(from, olen)) != 0) | ||
86 | return r; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | /* Minimal test_helper.c scaffholding to make this standalone */ | ||
91 | const char *in_test = NULL; | ||
92 | #define TEST_START(a) \ | ||
93 | do { \ | ||
94 | in_test = (a); \ | ||
95 | if (do_debug) \ | ||
96 | fprintf(stderr, "test %s starting\n", in_test); \ | ||
97 | } while (0) | ||
98 | #define TEST_DONE() \ | ||
99 | do { \ | ||
100 | if (do_debug) \ | ||
101 | fprintf(stderr, "test %s done\n", \ | ||
102 | in_test ? in_test : "???"); \ | ||
103 | in_test = NULL; \ | ||
104 | } while(0) | ||
105 | #define ASSERT_INT_EQ(a, b) \ | ||
106 | do { \ | ||
107 | if ((int)(a) != (int)(b)) { \ | ||
108 | fprintf(stderr, "%s %s:%d " \ | ||
109 | "%s (%d) != expected %s (%d)\n", \ | ||
110 | in_test ? in_test : "(none)", \ | ||
111 | __func__, __LINE__, #a, (int)(a), #b, (int)(b)); \ | ||
112 | exit(2); \ | ||
113 | } \ | ||
114 | } while (0) | ||
115 | #define ASSERT_INT_GE(a, b) \ | ||
116 | do { \ | ||
117 | if ((int)(a) < (int)(b)) { \ | ||
118 | fprintf(stderr, "%s %s:%d " \ | ||
119 | "%s (%d) < expected %s (%d)\n", \ | ||
120 | in_test ? in_test : "(none)", \ | ||
121 | __func__, __LINE__, #a, (int)(a), #b, (int)(b)); \ | ||
122 | exit(2); \ | ||
123 | } \ | ||
124 | } while (0) | ||
125 | #define ASSERT_PTR_NE(a, b) \ | ||
126 | do { \ | ||
127 | if ((a) == (b)) { \ | ||
128 | fprintf(stderr, "%s %s:%d " \ | ||
129 | "%s (%p) != expected %s (%p)\n", \ | ||
130 | in_test ? in_test : "(none)", \ | ||
131 | __func__, __LINE__, #a, (a), #b, (b)); \ | ||
132 | exit(2); \ | ||
133 | } \ | ||
134 | } while (0) | ||
135 | |||
136 | |||
137 | static void | ||
138 | run_kex(struct ssh *client, struct ssh *server, int *s2c, int *c2s, | ||
139 | int direction, int packet_index, | ||
140 | const char *dump_path, struct sshbuf *replace_data) | ||
141 | { | ||
142 | int r = 0; | ||
143 | |||
144 | while (!server->kex->done || !client->kex->done) { | ||
145 | if ((r = do_send_and_receive(server, client, S2C, s2c, | ||
146 | direction, packet_index, dump_path, replace_data))) | ||
147 | break; | ||
148 | if ((r = do_send_and_receive(client, server, C2S, c2s, | ||
149 | direction, packet_index, dump_path, replace_data))) | ||
150 | break; | ||
151 | } | ||
152 | if (do_debug) | ||
153 | printf("done: %s\n", ssh_err(r)); | ||
154 | ASSERT_INT_EQ(r, 0); | ||
155 | ASSERT_INT_EQ(server->kex->done, 1); | ||
156 | ASSERT_INT_EQ(client->kex->done, 1); | ||
157 | } | ||
158 | |||
159 | static void | ||
160 | do_kex_with_key(const char *kex, struct sshkey *prvkey, int *c2s, int *s2c, | ||
161 | int direction, int packet_index, | ||
162 | const char *dump_path, struct sshbuf *replace_data) | ||
163 | { | ||
164 | struct ssh *client = NULL, *server = NULL, *server2 = NULL; | ||
165 | struct sshkey *pubkey = NULL; | ||
166 | struct sshbuf *state; | ||
167 | struct kex_params kex_params; | ||
168 | char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; | ||
169 | char *keyname = NULL; | ||
170 | |||
171 | TEST_START("sshkey_from_private"); | ||
172 | ASSERT_INT_EQ(sshkey_from_private(prvkey, &pubkey), 0); | ||
173 | TEST_DONE(); | ||
174 | |||
175 | TEST_START("ssh_init"); | ||
176 | memcpy(kex_params.proposal, myproposal, sizeof(myproposal)); | ||
177 | if (kex != NULL) | ||
178 | kex_params.proposal[PROPOSAL_KEX_ALGS] = strdup(kex); | ||
179 | keyname = strdup(sshkey_ssh_name(prvkey)); | ||
180 | ASSERT_PTR_NE(keyname, NULL); | ||
181 | kex_params.proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = keyname; | ||
182 | ASSERT_INT_EQ(ssh_init(&client, 0, &kex_params), 0); | ||
183 | ASSERT_INT_EQ(ssh_init(&server, 1, &kex_params), 0); | ||
184 | ASSERT_PTR_NE(client, NULL); | ||
185 | ASSERT_PTR_NE(server, NULL); | ||
186 | TEST_DONE(); | ||
187 | |||
188 | TEST_START("ssh_add_hostkey"); | ||
189 | ASSERT_INT_EQ(ssh_add_hostkey(server, prvkey), 0); | ||
190 | ASSERT_INT_EQ(ssh_add_hostkey(client, pubkey), 0); | ||
191 | TEST_DONE(); | ||
192 | |||
193 | TEST_START("kex"); | ||
194 | run_kex(client, server, s2c, c2s, direction, packet_index, | ||
195 | dump_path, replace_data); | ||
196 | TEST_DONE(); | ||
197 | |||
198 | TEST_START("rekeying client"); | ||
199 | ASSERT_INT_EQ(kex_send_kexinit(client), 0); | ||
200 | run_kex(client, server, s2c, c2s, direction, packet_index, | ||
201 | dump_path, replace_data); | ||
202 | TEST_DONE(); | ||
203 | |||
204 | TEST_START("rekeying server"); | ||
205 | ASSERT_INT_EQ(kex_send_kexinit(server), 0); | ||
206 | run_kex(client, server, s2c, c2s, direction, packet_index, | ||
207 | dump_path, replace_data); | ||
208 | TEST_DONE(); | ||
209 | |||
210 | TEST_START("ssh_packet_get_state"); | ||
211 | state = sshbuf_new(); | ||
212 | ASSERT_PTR_NE(state, NULL); | ||
213 | ASSERT_INT_EQ(ssh_packet_get_state(server, state), 0); | ||
214 | ASSERT_INT_GE(sshbuf_len(state), 1); | ||
215 | TEST_DONE(); | ||
216 | |||
217 | TEST_START("ssh_packet_set_state"); | ||
218 | server2 = NULL; | ||
219 | ASSERT_INT_EQ(ssh_init(&server2, 1, NULL), 0); | ||
220 | ASSERT_PTR_NE(server2, NULL); | ||
221 | ASSERT_INT_EQ(ssh_add_hostkey(server2, prvkey), 0); | ||
222 | kex_free(server2->kex); /* XXX or should ssh_packet_set_state()? */ | ||
223 | ASSERT_INT_EQ(ssh_packet_set_state(server2, state), 0); | ||
224 | ASSERT_INT_EQ(sshbuf_len(state), 0); | ||
225 | sshbuf_free(state); | ||
226 | ASSERT_PTR_NE(server2->kex, NULL); | ||
227 | /* XXX we need to set the callbacks */ | ||
228 | server2->kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; | ||
229 | server2->kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; | ||
230 | server2->kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; | ||
231 | server2->kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; | ||
232 | #ifdef OPENSSL_HAS_ECC | ||
233 | server2->kex->kex[KEX_ECDH_SHA2] = kexecdh_server; | ||
234 | #endif | ||
235 | server2->kex->kex[KEX_C25519_SHA256] = kexc25519_server; | ||
236 | server2->kex->load_host_public_key = server->kex->load_host_public_key; | ||
237 | server2->kex->load_host_private_key = server->kex->load_host_private_key; | ||
238 | server2->kex->sign = server->kex->sign; | ||
239 | TEST_DONE(); | ||
240 | |||
241 | TEST_START("rekeying server2"); | ||
242 | ASSERT_INT_EQ(kex_send_kexinit(server2), 0); | ||
243 | run_kex(client, server2, s2c, c2s, direction, packet_index, | ||
244 | dump_path, replace_data); | ||
245 | ASSERT_INT_EQ(kex_send_kexinit(client), 0); | ||
246 | run_kex(client, server2, s2c, c2s, direction, packet_index, | ||
247 | dump_path, replace_data); | ||
248 | TEST_DONE(); | ||
249 | |||
250 | TEST_START("cleanup"); | ||
251 | sshkey_free(pubkey); | ||
252 | ssh_free(client); | ||
253 | ssh_free(server); | ||
254 | ssh_free(server2); | ||
255 | free(keyname); | ||
256 | TEST_DONE(); | ||
257 | } | ||
258 | |||
259 | static void | ||
260 | usage(void) | ||
261 | { | ||
262 | fprintf(stderr, | ||
263 | "Usage: kexfuzz [-hcdrv] [-D direction] [-f data_file]\n" | ||
264 | " [-K kex_alg] [-k private_key] [-i packet_index]\n" | ||
265 | "\n" | ||
266 | "Options:\n" | ||
267 | " -h Display this help\n" | ||
268 | " -c Count packets sent during KEX\n" | ||
269 | " -d Dump mode: record KEX packet to data file\n" | ||
270 | " -r Replace mode: replace packet with data file\n" | ||
271 | " -v Turn on verbose logging\n" | ||
272 | " -D S2C|C2S Packet direction for replacement or dump\n" | ||
273 | " -f data_file Path to data file for replacement or dump\n" | ||
274 | " -K kex_alg Name of KEX algorithm to test (see below)\n" | ||
275 | " -k private_key Path to private key file\n" | ||
276 | " -i packet_index Index of packet to replace or dump (from 0)\n" | ||
277 | "\n" | ||
278 | "Available KEX algorithms: %s\n", kex_alg_list(' ')); | ||
279 | } | ||
280 | |||
281 | static void | ||
282 | badusage(const char *bad) | ||
283 | { | ||
284 | fprintf(stderr, "Invalid options\n"); | ||
285 | fprintf(stderr, "%s\n", bad); | ||
286 | usage(); | ||
287 | exit(1); | ||
288 | } | ||
289 | |||
290 | int | ||
291 | main(int argc, char **argv) | ||
292 | { | ||
293 | int ch, fd, r; | ||
294 | int count_flag = 0, dump_flag = 0, replace_flag = 0; | ||
295 | int packet_index = -1, direction = -1; | ||
296 | int s2c = 0, c2s = 0; /* packet counts */ | ||
297 | const char *kex = NULL, *kpath = NULL, *data_path = NULL; | ||
298 | struct sshkey *key = NULL; | ||
299 | struct sshbuf *replace_data = NULL; | ||
300 | |||
301 | setvbuf(stdout, NULL, _IONBF, 0); | ||
302 | while ((ch = getopt(argc, argv, "hcdrvD:f:K:k:i:")) != -1) { | ||
303 | switch (ch) { | ||
304 | case 'h': | ||
305 | usage(); | ||
306 | return 0; | ||
307 | case 'c': | ||
308 | count_flag = 1; | ||
309 | break; | ||
310 | case 'd': | ||
311 | dump_flag = 1; | ||
312 | break; | ||
313 | case 'r': | ||
314 | replace_flag = 1; | ||
315 | break; | ||
316 | case 'v': | ||
317 | do_debug = 1; | ||
318 | break; | ||
319 | |||
320 | case 'D': | ||
321 | if (strcasecmp(optarg, "s2c") == 0) | ||
322 | direction = S2C; | ||
323 | else if (strcasecmp(optarg, "c2s") == 0) | ||
324 | direction = C2S; | ||
325 | else | ||
326 | badusage("Invalid direction (-D)"); | ||
327 | break; | ||
328 | case 'f': | ||
329 | data_path = optarg; | ||
330 | break; | ||
331 | case 'K': | ||
332 | kex = optarg; | ||
333 | break; | ||
334 | case 'k': | ||
335 | kpath = optarg; | ||
336 | break; | ||
337 | case 'i': | ||
338 | packet_index = atoi(optarg); | ||
339 | if (packet_index < 0) | ||
340 | badusage("Invalid packet index"); | ||
341 | break; | ||
342 | default: | ||
343 | badusage("unsupported flag"); | ||
344 | } | ||
345 | } | ||
346 | argc -= optind; | ||
347 | argv += optind; | ||
348 | |||
349 | /* Must select a single mode */ | ||
350 | if ((count_flag + dump_flag + replace_flag) != 1) | ||
351 | badusage("Must select one mode: -c, -d or -r"); | ||
352 | /* KEX type is mandatory */ | ||
353 | if (kex == NULL || !kex_names_valid(kex) || strchr(kex, ',') != NULL) | ||
354 | badusage("Missing or invalid kex type (-K flag)"); | ||
355 | /* Valid key is mandatory */ | ||
356 | if (kpath == NULL) | ||
357 | badusage("Missing private key (-k flag)"); | ||
358 | if ((fd = open(kpath, O_RDONLY)) == -1) | ||
359 | err(1, "open %s", kpath); | ||
360 | if ((r = sshkey_load_private_type_fd(fd, KEY_UNSPEC, NULL, | ||
361 | &key, NULL)) != 0) | ||
362 | errx(1, "Unable to load key %s: %s", kpath, ssh_err(r)); | ||
363 | close(fd); | ||
364 | /* XXX check that it is a private key */ | ||
365 | /* XXX support certificates */ | ||
366 | if (key == NULL || key->type == KEY_UNSPEC || key->type == KEY_RSA1) | ||
367 | badusage("Invalid key file (-k flag)"); | ||
368 | |||
369 | /* Replace (fuzz) mode */ | ||
370 | if (replace_flag) { | ||
371 | if (packet_index == -1 || direction == -1 || data_path == NULL) | ||
372 | badusage("Replace (-r) mode must specify direction " | ||
373 | "(-D) packet index (-i) and data path (-f)"); | ||
374 | if ((fd = open(data_path, O_RDONLY)) == -1) | ||
375 | err(1, "open %s", data_path); | ||
376 | replace_data = sshbuf_new(); | ||
377 | if ((r = sshkey_load_file(fd, replace_data)) != 0) | ||
378 | errx(1, "read %s: %s", data_path, ssh_err(r)); | ||
379 | close(fd); | ||
380 | } | ||
381 | |||
382 | /* Dump mode */ | ||
383 | if (dump_flag) { | ||
384 | if (packet_index == -1 || direction == -1 || data_path == NULL) | ||
385 | badusage("Dump (-d) mode must specify direction " | ||
386 | "(-D), packet index (-i) and data path (-f)"); | ||
387 | } | ||
388 | |||
389 | /* Count mode needs no further flags */ | ||
390 | |||
391 | do_kex_with_key(kex, key, &c2s, &s2c, | ||
392 | direction, packet_index, | ||
393 | dump_flag ? data_path : NULL, | ||
394 | replace_flag ? replace_data : NULL); | ||
395 | sshkey_free(key); | ||
396 | sshbuf_free(replace_data); | ||
397 | |||
398 | if (count_flag) { | ||
399 | printf("S2C: %d\n", s2c); | ||
400 | printf("C2S: %d\n", c2s); | ||
401 | } | ||
402 | |||
403 | return 0; | ||
404 | } | ||