blob: 04c1a4e3f0ae906e60d68ef90e864961208ea03b [file] [log] [blame]
David Benjamin025b3d32014-07-01 19:53:04 -04001/* Copyright (c) 2014, Google Inc.
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
Adam Langleyded93582014-07-31 15:23:51 -070015#include <openssl/base.h>
16
17#if !defined(OPENSSL_WINDOWS)
David Benjamin025b3d32014-07-01 19:53:04 -040018#include <arpa/inet.h>
David Benjamin1d5c83e2014-07-22 19:20:02 -040019#include <netinet/in.h>
20#include <signal.h>
21#include <sys/socket.h>
David Benjamin8f2c20e2014-07-09 09:30:38 -040022#include <unistd.h>
Adam Langleyded93582014-07-31 15:23:51 -070023#endif
24
25#include <sys/types.h>
David Benjamin025b3d32014-07-01 19:53:04 -040026
David Benjamin025b3d32014-07-01 19:53:04 -040027#include <openssl/bio.h>
David Benjamin8f2c20e2014-07-09 09:30:38 -040028#include <openssl/bytestring.h>
David Benjamin1d5c83e2014-07-22 19:20:02 -040029#include <openssl/ssl.h>
30
David Benjamin43ec06f2014-08-05 02:28:57 -040031#include "async_bio.h"
32
David Benjamin1d5c83e2014-07-22 19:20:02 -040033static int usage(const char *program) {
34 fprintf(stderr, "Usage: %s (client|server) (normal|resume) [flags...]\n",
35 program);
36 return 1;
37}
David Benjamin025b3d32014-07-01 19:53:04 -040038
David Benjamin1f5f62b2014-07-12 16:18:02 -040039static const char *expected_server_name = NULL;
40static int early_callback_called = 0;
David Benjamin8f2c20e2014-07-09 09:30:38 -040041
David Benjamin1f5f62b2014-07-12 16:18:02 -040042static int select_certificate_callback(const struct ssl_early_callback_ctx *ctx) {
David Benjamin8f2c20e2014-07-09 09:30:38 -040043 early_callback_called = 1;
44
David Benjamin7b030512014-07-08 17:30:11 -040045 if (!expected_server_name) {
46 return 1;
47 }
David Benjamin8f2c20e2014-07-09 09:30:38 -040048
David Benjamin7b030512014-07-08 17:30:11 -040049 const uint8_t *extension_data;
50 size_t extension_len;
51 CBS extension, server_name_list, host_name;
52 uint8_t name_type;
David Benjamin8f2c20e2014-07-09 09:30:38 -040053
David Benjamin7b030512014-07-08 17:30:11 -040054 if (!SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_server_name,
55 &extension_data,
56 &extension_len)) {
57 fprintf(stderr, "Could not find server_name extension.\n");
58 return -1;
59 }
David Benjamin8f2c20e2014-07-09 09:30:38 -040060
David Benjamin7b030512014-07-08 17:30:11 -040061 CBS_init(&extension, extension_data, extension_len);
62 if (!CBS_get_u16_length_prefixed(&extension, &server_name_list) ||
63 CBS_len(&extension) != 0 ||
64 !CBS_get_u8(&server_name_list, &name_type) ||
65 name_type != TLSEXT_NAMETYPE_host_name ||
66 !CBS_get_u16_length_prefixed(&server_name_list, &host_name) ||
67 CBS_len(&server_name_list) != 0) {
68 fprintf(stderr, "Could not decode server_name extension.\n");
69 return -1;
70 }
71
David Benjamin22f9bcc2014-07-13 12:29:21 -040072 if (!CBS_mem_equal(&host_name, (const uint8_t*)expected_server_name,
73 strlen(expected_server_name))) {
David Benjamin7b030512014-07-08 17:30:11 -040074 fprintf(stderr, "Server name mismatch.\n");
David Benjamin8f2c20e2014-07-09 09:30:38 -040075 }
76
77 return 1;
78}
David Benjamin025b3d32014-07-01 19:53:04 -040079
David Benjamin1f5f62b2014-07-12 16:18:02 -040080static int skip_verify(int preverify_ok, X509_STORE_CTX *store_ctx) {
David Benjamin67666e72014-07-12 15:47:52 -040081 return 1;
82}
83
David Benjamin1f5f62b2014-07-12 16:18:02 -040084static const char *advertise_npn = NULL;
85
86static int next_protos_advertised_callback(SSL *ssl,
David Benjamin7e3305e2014-07-28 14:52:32 -040087 const uint8_t **out,
88 unsigned int *out_len,
89 void *arg) {
David Benjamin1f5f62b2014-07-12 16:18:02 -040090 if (!advertise_npn)
91 return SSL_TLSEXT_ERR_NOACK;
92
93 // TODO(davidben): Support passing byte strings with NULs to the
94 // test shim.
95 *out = (const uint8_t*)advertise_npn;
96 *out_len = strlen(advertise_npn);
97 return SSL_TLSEXT_ERR_OK;
98}
99
David Benjamin7e3305e2014-07-28 14:52:32 -0400100static const char *select_next_proto = NULL;
101
102static int next_proto_select_callback(SSL* ssl,
103 uint8_t** out,
104 uint8_t* outlen,
105 const uint8_t* in,
106 unsigned inlen,
107 void* arg) {
108 if (!select_next_proto)
109 return SSL_TLSEXT_ERR_NOACK;
110
111 *out = (uint8_t*)select_next_proto;
112 *outlen = strlen(select_next_proto);
113 return SSL_TLSEXT_ERR_OK;
114}
115
David Benjamin1d5c83e2014-07-22 19:20:02 -0400116static SSL_CTX *setup_ctx(int is_server) {
David Benjamin025b3d32014-07-01 19:53:04 -0400117 if (!SSL_library_init()) {
118 return NULL;
119 }
120
121 SSL_CTX *ssl_ctx = NULL;
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400122 DH *dh = NULL;
David Benjamin025b3d32014-07-01 19:53:04 -0400123
124 ssl_ctx = SSL_CTX_new(
125 is_server ? SSLv23_server_method() : SSLv23_client_method());
126 if (ssl_ctx == NULL) {
127 goto err;
128 }
129
130 if (!SSL_CTX_set_ecdh_auto(ssl_ctx, 1)) {
131 goto err;
132 }
133
134 if (!SSL_CTX_set_cipher_list(ssl_ctx, "ALL")) {
135 goto err;
136 }
137
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400138 dh = DH_get_2048_256(NULL);
139 if (!SSL_CTX_set_tmp_dh(ssl_ctx, dh)) {
140 goto err;
141 }
142
David Benjamin1d5c83e2014-07-22 19:20:02 -0400143 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
144
David Benjamin8f2c20e2014-07-09 09:30:38 -0400145 ssl_ctx->select_certificate_cb = select_certificate_callback;
146
David Benjamin1f5f62b2014-07-12 16:18:02 -0400147 SSL_CTX_set_next_protos_advertised_cb(
148 ssl_ctx, next_protos_advertised_callback, NULL);
David Benjamin7e3305e2014-07-28 14:52:32 -0400149 SSL_CTX_set_next_proto_select_cb(
150 ssl_ctx, next_proto_select_callback, NULL);
David Benjamin1f5f62b2014-07-12 16:18:02 -0400151
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400152 DH_free(dh);
David Benjamin1d5c83e2014-07-22 19:20:02 -0400153 return ssl_ctx;
154
155 err:
David Benjaminf4e5c4e2014-08-02 17:35:45 -0400156 if (dh != NULL) {
157 DH_free(dh);
158 }
David Benjamin1d5c83e2014-07-22 19:20:02 -0400159 if (ssl_ctx != NULL) {
160 SSL_CTX_free(ssl_ctx);
161 }
162 return NULL;
163}
164
David Benjamin43ec06f2014-08-05 02:28:57 -0400165static int retry_async(SSL *ssl, int ret, BIO *bio) {
166 // No error; don't retry.
167 if (ret >= 0) {
168 return 0;
David Benjamin025b3d32014-07-01 19:53:04 -0400169 }
David Benjamin43ec06f2014-08-05 02:28:57 -0400170 // See if we needed to read or write more. If so, allow one byte through on
171 // the appropriate end to maximally stress the state machine.
172 int err = SSL_get_error(ssl, ret);
173 if (err == SSL_ERROR_WANT_READ) {
174 async_bio_allow_read(bio, 1);
175 return 1;
176 } else if (err == SSL_ERROR_WANT_WRITE) {
177 async_bio_allow_write(bio, 1);
178 return 1;
David Benjamin025b3d32014-07-01 19:53:04 -0400179 }
David Benjamin43ec06f2014-08-05 02:28:57 -0400180 return 0;
David Benjamin025b3d32014-07-01 19:53:04 -0400181}
182
David Benjamin1d5c83e2014-07-22 19:20:02 -0400183static int do_exchange(SSL_SESSION **out_session,
184 SSL_CTX *ssl_ctx,
185 int argc,
186 char **argv,
187 int is_server,
188 int is_resume,
189 int fd,
190 SSL_SESSION *session) {
Kenny Root7fdeaf12014-08-05 15:23:37 -0700191 bool async = false, write_different_record_sizes = false;
David Benjamin7b030512014-07-08 17:30:11 -0400192 const char *expected_certificate_types = NULL;
David Benjamin1f5f62b2014-07-12 16:18:02 -0400193 const char *expected_next_proto = NULL;
David Benjamin1d5c83e2014-07-22 19:20:02 -0400194 expected_server_name = NULL;
195 early_callback_called = 0;
196 advertise_npn = NULL;
David Benjamin025b3d32014-07-01 19:53:04 -0400197
David Benjamin43ec06f2014-08-05 02:28:57 -0400198 SSL *ssl = SSL_new(ssl_ctx);
David Benjamin025b3d32014-07-01 19:53:04 -0400199 if (ssl == NULL) {
200 BIO_print_errors_fp(stdout);
201 return 1;
202 }
203
David Benjamin1d5c83e2014-07-22 19:20:02 -0400204 for (int i = 0; i < argc; i++) {
David Benjamin025b3d32014-07-01 19:53:04 -0400205 if (strcmp(argv[i], "-fallback-scsv") == 0) {
206 if (!SSL_enable_fallback_scsv(ssl)) {
207 BIO_print_errors_fp(stdout);
208 return 1;
209 }
210 } else if (strcmp(argv[i], "-key-file") == 0) {
211 i++;
212 if (i >= argc) {
213 fprintf(stderr, "Missing parameter\n");
214 return 1;
215 }
216 if (!SSL_use_PrivateKey_file(ssl, argv[i], SSL_FILETYPE_PEM)) {
217 BIO_print_errors_fp(stdout);
218 return 1;
219 }
220 } else if (strcmp(argv[i], "-cert-file") == 0) {
221 i++;
222 if (i >= argc) {
223 fprintf(stderr, "Missing parameter\n");
224 return 1;
225 }
226 if (!SSL_use_certificate_file(ssl, argv[i], SSL_FILETYPE_PEM)) {
227 BIO_print_errors_fp(stdout);
228 return 1;
229 }
David Benjamin197b3ab2014-07-02 18:37:33 -0400230 } else if (strcmp(argv[i], "-expect-server-name") == 0) {
231 i++;
232 if (i >= argc) {
233 fprintf(stderr, "Missing parameter\n");
234 return 1;
235 }
236 expected_server_name = argv[i];
David Benjamin7b030512014-07-08 17:30:11 -0400237 } else if (strcmp(argv[i], "-expect-certificate-types") == 0) {
238 i++;
239 if (i >= argc) {
240 fprintf(stderr, "Missing parameter\n");
241 return 1;
242 }
243 // Conveniently, 00 is not a certificate type.
244 expected_certificate_types = argv[i];
David Benjamin67666e72014-07-12 15:47:52 -0400245 } else if (strcmp(argv[i], "-require-any-client-certificate") == 0) {
246 SSL_set_verify(ssl, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
247 skip_verify);
David Benjamin1f5f62b2014-07-12 16:18:02 -0400248 } else if (strcmp(argv[i], "-advertise-npn") == 0) {
249 i++;
250 if (i >= argc) {
251 fprintf(stderr, "Missing parameter\n");
252 return 1;
253 }
254 advertise_npn = argv[i];
255 } else if (strcmp(argv[i], "-expect-next-proto") == 0) {
256 i++;
257 if (i >= argc) {
258 fprintf(stderr, "Missing parameter\n");
259 return 1;
260 }
261 expected_next_proto = argv[i];
David Benjamin7e3305e2014-07-28 14:52:32 -0400262 } else if (strcmp(argv[i], "-false-start") == 0) {
263 SSL_set_mode(ssl, SSL_MODE_HANDSHAKE_CUTTHROUGH);
264 } else if (strcmp(argv[i], "-select-next-proto") == 0) {
265 i++;
266 if (i >= argc) {
267 fprintf(stderr, "Missing parameter\n");
268 return 1;
269 }
270 select_next_proto = argv[i];
David Benjamin43ec06f2014-08-05 02:28:57 -0400271 } else if (strcmp(argv[i], "-async") == 0) {
Kenny Root7fdeaf12014-08-05 15:23:37 -0700272 async = true;
273 } else if (strcmp(argv[i], "-write-different-record-sizes") == 0) {
274 write_different_record_sizes = true;
275 } else if (strcmp(argv[i], "-cbc-record-splitting") == 0) {
276 SSL_set_mode(ssl, SSL_MODE_CBC_RECORD_SPLITTING);
277 } else if (strcmp(argv[i], "-partial-write") == 0) {
278 SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
David Benjamin7e2e6cf2014-08-07 17:44:24 -0400279 } else if (strcmp(argv[i], "-no-tls12") == 0) {
280 SSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
281 } else if (strcmp(argv[i], "-no-tls11") == 0) {
282 SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
283 } else if (strcmp(argv[i], "-no-tls1") == 0) {
284 SSL_set_options(ssl, SSL_OP_NO_TLSv1);
285 } else if (strcmp(argv[i], "-no-ssl3") == 0) {
286 SSL_set_options(ssl, SSL_OP_NO_SSLv3);
David Benjamin025b3d32014-07-01 19:53:04 -0400287 } else {
288 fprintf(stderr, "Unknown argument: %s\n", argv[i]);
289 return 1;
290 }
291 }
292
David Benjamin43ec06f2014-08-05 02:28:57 -0400293 BIO *bio = BIO_new_fd(fd, 1 /* take ownership */);
294 if (bio == NULL) {
295 BIO_print_errors_fp(stdout);
296 return 1;
297 }
298 if (async) {
299 BIO *async = async_bio_create();
300 BIO_push(async, bio);
301 bio = async;
302 }
303 SSL_set_bio(ssl, bio, bio);
304
David Benjamin1d5c83e2014-07-22 19:20:02 -0400305 if (session != NULL) {
306 if (SSL_set_session(ssl, session) != 1) {
307 fprintf(stderr, "failed to set session\n");
308 return 2;
309 }
310 }
311
312 int ret;
David Benjamin43ec06f2014-08-05 02:28:57 -0400313 do {
314 if (is_server) {
315 ret = SSL_accept(ssl);
316 } else {
317 ret = SSL_connect(ssl);
318 }
319 } while (async && retry_async(ssl, ret, bio));
David Benjamin025b3d32014-07-01 19:53:04 -0400320 if (ret != 1) {
321 SSL_free(ssl);
322 BIO_print_errors_fp(stdout);
323 return 2;
324 }
325
David Benjamin1d5c83e2014-07-22 19:20:02 -0400326 if (is_resume && !SSL_session_reused(ssl)) {
327 fprintf(stderr, "session was not reused\n");
328 return 2;
329 }
330
David Benjamin197b3ab2014-07-02 18:37:33 -0400331 if (expected_server_name) {
332 const char *server_name =
333 SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
334 if (strcmp(server_name, expected_server_name) != 0) {
335 fprintf(stderr, "servername mismatch (got %s; want %s)\n",
336 server_name, expected_server_name);
337 return 2;
338 }
David Benjamin8f2c20e2014-07-09 09:30:38 -0400339
340 if (!early_callback_called) {
341 fprintf(stderr, "early callback not called\n");
342 return 2;
343 }
David Benjamin197b3ab2014-07-02 18:37:33 -0400344 }
345
David Benjamin7b030512014-07-08 17:30:11 -0400346 if (expected_certificate_types) {
347 uint8_t *certificate_types;
348 int num_certificate_types =
349 SSL_get0_certificate_types(ssl, &certificate_types);
350 if (num_certificate_types != (int)strlen(expected_certificate_types) ||
351 memcmp(certificate_types,
352 expected_certificate_types,
353 num_certificate_types) != 0) {
354 fprintf(stderr, "certificate types mismatch\n");
355 return 2;
356 }
357 }
358
David Benjamin1f5f62b2014-07-12 16:18:02 -0400359 if (expected_next_proto) {
360 const uint8_t *next_proto;
361 unsigned next_proto_len;
362 SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
363 if (next_proto_len != strlen(expected_next_proto) ||
364 memcmp(next_proto, expected_next_proto, next_proto_len) != 0) {
365 fprintf(stderr, "negotiated next proto mismatch\n");
366 return 2;
367 }
368 }
369
Kenny Root7fdeaf12014-08-05 15:23:37 -0700370 if (write_different_record_sizes) {
371 // This mode writes a number of different record sizes in an attempt to
372 // trip up the CBC record splitting code.
373 uint8_t buf[32769];
374 memset(buf, 0x42, sizeof(buf));
375 static const size_t kRecordSizes[] = {
376 0, 1, 255, 256, 257, 16383, 16384, 16385, 32767, 32768, 32769};
377 for (size_t i = 0; i < sizeof(kRecordSizes) / sizeof(kRecordSizes[0]);
378 i++) {
David Benjamin43ec06f2014-08-05 02:28:57 -0400379 int w;
Kenny Root7fdeaf12014-08-05 15:23:37 -0700380 const size_t len = kRecordSizes[i];
381 size_t off = 0;
382
383 if (len > sizeof(buf)) {
384 fprintf(stderr, "Bad kRecordSizes value.\n");
385 return 5;
386 }
387
David Benjamin43ec06f2014-08-05 02:28:57 -0400388 do {
Kenny Root7fdeaf12014-08-05 15:23:37 -0700389 w = SSL_write(ssl, buf + off, len - off);
390 if (w > 0) {
391 off += (size_t) w;
392 }
393 } while ((async && retry_async(ssl, w, bio)) || (w > 0 && off < len));
394
395 if (w < 0 || off != len) {
David Benjamin025b3d32014-07-01 19:53:04 -0400396 SSL_free(ssl);
397 BIO_print_errors_fp(stdout);
398 return 4;
399 }
400 }
Kenny Root7fdeaf12014-08-05 15:23:37 -0700401 } else {
402 for (;;) {
403 uint8_t buf[512];
404 int n;
405 do {
406 n = SSL_read(ssl, buf, sizeof(buf));
407 } while (async && retry_async(ssl, n, bio));
408 if (n < 0) {
409 SSL_free(ssl);
410 BIO_print_errors_fp(stdout);
411 return 3;
412 } else if (n == 0) {
413 break;
414 } else {
415 for (int i = 0; i < n; i++) {
416 buf[i] ^= 0xff;
417 }
418 int w;
419 do {
420 w = SSL_write(ssl, buf, n);
421 } while (async && retry_async(ssl, w, bio));
422 if (w != n) {
423 SSL_free(ssl);
424 BIO_print_errors_fp(stdout);
425 return 4;
426 }
427 }
428 }
David Benjamin025b3d32014-07-01 19:53:04 -0400429 }
430
David Benjamin1d5c83e2014-07-22 19:20:02 -0400431 if (out_session) {
432 *out_session = SSL_get1_session(ssl);
433 }
434
435 SSL_shutdown(ssl);
David Benjamin025b3d32014-07-01 19:53:04 -0400436 SSL_free(ssl);
437 return 0;
438}
David Benjamin1d5c83e2014-07-22 19:20:02 -0400439
440int main(int argc, char **argv) {
441 int is_server, resume;
442
Adam Langleyded93582014-07-31 15:23:51 -0700443#if !defined(OPENSSL_WINDOWS)
David Benjamin1d5c83e2014-07-22 19:20:02 -0400444 signal(SIGPIPE, SIG_IGN);
Adam Langleyded93582014-07-31 15:23:51 -0700445#endif
David Benjamin1d5c83e2014-07-22 19:20:02 -0400446
447 if (argc < 3) {
448 return usage(argv[0]);
449 }
450
451 if (strcmp(argv[1], "client") == 0) {
452 is_server = 0;
453 } else if (strcmp(argv[1], "server") == 0) {
454 is_server = 1;
455 } else {
456 return usage(argv[0]);
457 }
458
459 if (strcmp(argv[2], "normal") == 0) {
460 resume = 0;
461 } else if (strcmp(argv[2], "resume") == 0) {
462 resume = 1;
463 } else {
464 return usage(argv[0]);
465 }
466
467 SSL_CTX *ssl_ctx = setup_ctx(is_server);
468 if (ssl_ctx == NULL) {
469 BIO_print_errors_fp(stdout);
470 return 1;
471 }
472
473 SSL_SESSION *session;
474 int ret = do_exchange(&session,
475 ssl_ctx,
476 argc - 3, argv + 3,
477 is_server, 0 /* is_resume */,
478 3 /* fd */, NULL /* session */);
479 if (ret != 0) {
480 return ret;
481 }
482
483 if (resume) {
484 int ret = do_exchange(NULL,
485 ssl_ctx, argc - 3, argv + 3,
486 is_server, 1 /* is_resume */,
487 4 /* fd */,
488 is_server ? NULL : session);
489 if (ret != 0) {
490 return ret;
491 }
492 }
493
494 SSL_SESSION_free(session);
495 SSL_CTX_free(ssl_ctx);
496 return 0;
497}