]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - crypto/openssh/regress/misc/kexfuzz/kexfuzz.c
Upgrade OpenSSH to 7.3p1.
[FreeBSD/stable/10.git] / crypto / openssh / regress / misc / kexfuzz / kexfuzz.c
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 "includes.h"
9
10 #include <sys/types.h>
11 #include <sys/param.h>
12 #include <stdio.h>
13 #ifdef HAVE_STDINT_H
14 # include <stdint.h>
15 #endif
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 #ifdef HAVE_ERR_H
21 # include <err.h>
22 #endif
23
24 #include "ssherr.h"
25 #include "ssh_api.h"
26 #include "sshbuf.h"
27 #include "packet.h"
28 #include "myproposal.h"
29 #include "authfile.h"
30
31 struct ssh *active_state = NULL; /* XXX - needed for linking */
32
33 void kex_tests(void);
34 static int do_debug = 0;
35
36 enum direction { S2C, C2S };
37
38 static int
39 do_send_and_receive(struct ssh *from, struct ssh *to, int mydirection,
40     int *packet_count, int trigger_direction, int packet_index,
41     const char *dump_path, struct sshbuf *replace_data)
42 {
43         u_char type;
44         size_t len, olen;
45         const u_char *buf;
46         int r;
47         FILE *dumpfile;
48
49         for (;;) {
50                 if ((r = ssh_packet_next(from, &type)) != 0) {
51                         fprintf(stderr, "ssh_packet_next: %s\n", ssh_err(r));
52                         return r;
53                 }
54                 if (type != 0)
55                         return 0;
56                 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)
89                         return 0;
90                 if ((r = ssh_input_append(to, buf, len)) != 0 ||
91                     (r = ssh_output_consume(from, olen)) != 0)
92                         return r;
93         }
94 }
95
96 /* Minimal test_helper.c scaffholding to make this standalone */
97 const char *in_test = NULL;
98 #define TEST_START(a)   \
99         do { \
100                 in_test = (a); \
101                 if (do_debug) \
102                         fprintf(stderr, "test %s starting\n", in_test); \
103         } while (0)
104 #define TEST_DONE()     \
105         do { \
106                 if (do_debug) \
107                         fprintf(stderr, "test %s done\n", \
108                             in_test ? in_test : "???"); \
109                 in_test = NULL; \
110         } while(0)
111 #define ASSERT_INT_EQ(a, b) \
112         do { \
113                 if ((int)(a) != (int)(b)) { \
114                         fprintf(stderr, "%s %s:%d " \
115                             "%s (%d) != expected %s (%d)\n", \
116                             in_test ? in_test : "(none)", \
117                             __func__, __LINE__, #a, (int)(a), #b, (int)(b)); \
118                         exit(2); \
119                 } \
120         } while (0)
121 #define ASSERT_INT_GE(a, b) \
122         do { \
123                 if ((int)(a) < (int)(b)) { \
124                         fprintf(stderr, "%s %s:%d " \
125                             "%s (%d) < expected %s (%d)\n", \
126                             in_test ? in_test : "(none)", \
127                             __func__, __LINE__, #a, (int)(a), #b, (int)(b)); \
128                         exit(2); \
129                 } \
130         } while (0)
131 #define ASSERT_PTR_NE(a, b) \
132         do { \
133                 if ((a) == (b)) { \
134                         fprintf(stderr, "%s %s:%d " \
135                             "%s (%p) != expected %s (%p)\n", \
136                             in_test ? in_test : "(none)", \
137                             __func__, __LINE__, #a, (a), #b, (b)); \
138                         exit(2); \
139                 } \
140         } while (0)
141
142
143 static void
144 run_kex(struct ssh *client, struct ssh *server, int *s2c, int *c2s,
145     int direction, int packet_index,
146     const char *dump_path, struct sshbuf *replace_data)
147 {
148         int r = 0;
149
150         while (!server->kex->done || !client->kex->done) {
151                 if ((r = do_send_and_receive(server, client, S2C, s2c,
152                     direction, packet_index, dump_path, replace_data)))
153                         break;
154                 if ((r = do_send_and_receive(client, server, C2S, c2s,
155                     direction, packet_index, dump_path, replace_data)))
156                         break;
157         }
158         if (do_debug)
159                 printf("done: %s\n", ssh_err(r));
160         ASSERT_INT_EQ(r, 0);
161         ASSERT_INT_EQ(server->kex->done, 1);
162         ASSERT_INT_EQ(client->kex->done, 1);
163 }
164
165 static void
166 do_kex_with_key(const char *kex, struct sshkey *prvkey, int *c2s, int *s2c,
167     int direction, int packet_index,
168     const char *dump_path, struct sshbuf *replace_data)
169 {
170         struct ssh *client = NULL, *server = NULL, *server2 = NULL;
171         struct sshkey *pubkey = NULL;
172         struct sshbuf *state;
173         struct kex_params kex_params;
174         char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT };
175         char *keyname = NULL;
176
177         TEST_START("sshkey_from_private");
178         ASSERT_INT_EQ(sshkey_from_private(prvkey, &pubkey), 0);
179         TEST_DONE();
180
181         TEST_START("ssh_init");
182         memcpy(kex_params.proposal, myproposal, sizeof(myproposal));
183         if (kex != NULL)
184                 kex_params.proposal[PROPOSAL_KEX_ALGS] = strdup(kex);
185         keyname = strdup(sshkey_ssh_name(prvkey));
186         ASSERT_PTR_NE(keyname, NULL);
187         kex_params.proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = keyname;
188         ASSERT_INT_EQ(ssh_init(&client, 0, &kex_params), 0);
189         ASSERT_INT_EQ(ssh_init(&server, 1, &kex_params), 0);
190         ASSERT_PTR_NE(client, NULL);
191         ASSERT_PTR_NE(server, NULL);
192         TEST_DONE();
193
194         TEST_START("ssh_add_hostkey");
195         ASSERT_INT_EQ(ssh_add_hostkey(server, prvkey), 0);
196         ASSERT_INT_EQ(ssh_add_hostkey(client, pubkey), 0);
197         TEST_DONE();
198
199         TEST_START("kex");
200         run_kex(client, server, s2c, c2s, direction, packet_index,
201             dump_path, replace_data);
202         TEST_DONE();
203
204         TEST_START("rekeying client");
205         ASSERT_INT_EQ(kex_send_kexinit(client), 0);
206         run_kex(client, server, s2c, c2s, direction, packet_index,
207             dump_path, replace_data);
208         TEST_DONE();
209
210         TEST_START("rekeying server");
211         ASSERT_INT_EQ(kex_send_kexinit(server), 0);
212         run_kex(client, server, s2c, c2s, direction, packet_index,
213             dump_path, replace_data);
214         TEST_DONE();
215
216         TEST_START("ssh_packet_get_state");
217         state = sshbuf_new();
218         ASSERT_PTR_NE(state, NULL);
219         ASSERT_INT_EQ(ssh_packet_get_state(server, state), 0);
220         ASSERT_INT_GE(sshbuf_len(state), 1);
221         TEST_DONE();
222
223         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);
228         kex_free(server2->kex); /* XXX or should ssh_packet_set_state()? */
229         ASSERT_INT_EQ(ssh_packet_set_state(server2, state), 0);
230         ASSERT_INT_EQ(sshbuf_len(state), 0);
231         sshbuf_free(state);
232         ASSERT_PTR_NE(server2->kex, NULL);
233         /* XXX we need to set the callbacks */
234         server2->kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
235         server2->kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server;
236         server2->kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
237         server2->kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
238 #ifdef OPENSSL_HAS_ECC
239         server2->kex->kex[KEX_ECDH_SHA2] = kexecdh_server;
240 #endif
241         server2->kex->kex[KEX_C25519_SHA256] = kexc25519_server;
242         server2->kex->load_host_public_key = server->kex->load_host_public_key;
243         server2->kex->load_host_private_key = server->kex->load_host_private_key;
244         server2->kex->sign = server->kex->sign;
245         TEST_DONE();
246
247         TEST_START("rekeying server2");
248         ASSERT_INT_EQ(kex_send_kexinit(server2), 0);
249         run_kex(client, server2, s2c, c2s, direction, packet_index,
250             dump_path, replace_data);
251         ASSERT_INT_EQ(kex_send_kexinit(client), 0);
252         run_kex(client, server2, s2c, c2s, direction, packet_index,
253             dump_path, replace_data);
254         TEST_DONE();
255
256         TEST_START("cleanup");
257         sshkey_free(pubkey);
258         ssh_free(client);
259         ssh_free(server);
260         ssh_free(server2);
261         free(keyname);
262         TEST_DONE();
263 }
264
265 static void
266 usage(void)
267 {
268         fprintf(stderr,
269             "Usage: kexfuzz [-hcdrv] [-D direction] [-f data_file]\n"
270             "               [-K kex_alg] [-k private_key] [-i packet_index]\n"
271             "\n"
272             "Options:\n"
273             "    -h               Display this help\n"
274             "    -c               Count packets sent during KEX\n"
275             "    -d               Dump mode: record KEX packet to data file\n"
276             "    -r               Replace mode: replace packet with data file\n"
277             "    -v               Turn on verbose logging\n"
278             "    -D S2C|C2S       Packet direction for replacement or dump\n"
279             "    -f data_file     Path to data file for replacement or dump\n"
280             "    -K kex_alg       Name of KEX algorithm to test (see below)\n"
281             "    -k private_key   Path to private key file\n"
282             "    -i packet_index  Index of packet to replace or dump (from 0)\n"
283             "\n"
284             "Available KEX algorithms: %s\n", kex_alg_list(' '));
285 }
286
287 static void
288 badusage(const char *bad)
289 {
290         fprintf(stderr, "Invalid options\n");
291         fprintf(stderr, "%s\n", bad);
292         usage();
293         exit(1);
294 }
295
296 int
297 main(int argc, char **argv)
298 {
299         int ch, fd, r;
300         int count_flag = 0, dump_flag = 0, replace_flag = 0;
301         int packet_index = -1, direction = -1;
302         int s2c = 0, c2s = 0; /* packet counts */
303         const char *kex = NULL, *kpath = NULL, *data_path = NULL;
304         struct sshkey *key = NULL;
305         struct sshbuf *replace_data = NULL;
306
307         setvbuf(stdout, NULL, _IONBF, 0);
308         while ((ch = getopt(argc, argv, "hcdrvD:f:K:k:i:")) != -1) {
309                 switch (ch) {
310                 case 'h':
311                         usage();
312                         return 0;
313                 case 'c':
314                         count_flag = 1;
315                         break;
316                 case 'd':
317                         dump_flag = 1;
318                         break;
319                 case 'r':
320                         replace_flag = 1;
321                         break;
322                 case 'v':
323                         do_debug = 1;
324                         break;
325
326                 case 'D':
327                         if (strcasecmp(optarg, "s2c") == 0)
328                                 direction = S2C;
329                         else if (strcasecmp(optarg, "c2s") == 0)
330                                 direction = C2S;
331                         else
332                                 badusage("Invalid direction (-D)");
333                         break;
334                 case 'f':
335                         data_path = optarg;
336                         break;
337                 case 'K':
338                         kex = optarg;
339                         break;
340                 case 'k':
341                         kpath = optarg;
342                         break;
343                 case 'i':
344                         packet_index = atoi(optarg);
345                         if (packet_index < 0)
346                                 badusage("Invalid packet index");
347                         break;
348                 default:
349                         badusage("unsupported flag");
350                 }
351         }
352         argc -= optind;
353         argv += optind;
354
355         /* Must select a single mode */
356         if ((count_flag + dump_flag + replace_flag) != 1)
357                 badusage("Must select one mode: -c, -d or -r");
358         /* KEX type is mandatory */
359         if (kex == NULL || !kex_names_valid(kex) || strchr(kex, ',') != NULL)
360                 badusage("Missing or invalid kex type (-K flag)");
361         /* Valid key is mandatory */
362         if (kpath == NULL)
363                 badusage("Missing private key (-k flag)");
364         if ((fd = open(kpath, O_RDONLY)) == -1)
365                 err(1, "open %s", kpath);
366         if ((r = sshkey_load_private_type_fd(fd, KEY_UNSPEC, NULL,
367             &key, NULL)) != 0)
368                 errx(1, "Unable to load key %s: %s", kpath, ssh_err(r));
369         close(fd);
370         /* XXX check that it is a private key */
371         /* XXX support certificates */
372         if (key == NULL || key->type == KEY_UNSPEC || key->type == KEY_RSA1)
373                 badusage("Invalid key file (-k flag)");
374
375         /* Replace (fuzz) mode */
376         if (replace_flag) {
377                 if (packet_index == -1 || direction == -1 || data_path == NULL)
378                         badusage("Replace (-r) mode must specify direction "
379                             "(-D) packet index (-i) and data path (-f)");
380                 if ((fd = open(data_path, O_RDONLY)) == -1)
381                         err(1, "open %s", data_path);
382                 replace_data = sshbuf_new();
383                 if ((r = sshkey_load_file(fd, replace_data)) != 0)
384                         errx(1, "read %s: %s", data_path, ssh_err(r));
385                 close(fd);
386         }
387
388         /* Dump mode */
389         if (dump_flag) {
390                 if (packet_index == -1 || direction == -1 || data_path == NULL)
391                         badusage("Dump (-d) mode must specify direction "
392                             "(-D), packet index (-i) and data path (-f)");
393         }
394
395         /* Count mode needs no further flags */
396
397         do_kex_with_key(kex, key, &c2s, &s2c,
398             direction, packet_index,
399             dump_flag ? data_path : NULL,
400             replace_flag ? replace_data : NULL);
401         sshkey_free(key);
402         sshbuf_free(replace_data);
403
404         if (count_flag) {
405                 printf("S2C: %d\n", s2c);
406                 printf("C2S: %d\n", c2s);
407         }
408
409         return 0;
410 }