1/* test/heartbeat_test.c */ 2/*- 3 * Unit test for TLS heartbeats. 4 * 5 * Acts as a regression test against the Heartbleed bug (CVE-2014-0160). 6 * 7 * Author: Mike Bland (mbland@acm.org, http://mike-bland.com/) 8 * Date: 2014-04-12 9 * License: Creative Commons Attribution 4.0 International (CC By 4.0) 10 * http://creativecommons.org/licenses/by/4.0/deed.en_US 11 * 12 * OUTPUT 13 * ------ 14 * The program returns zero on success. It will print a message with a count 15 * of the number of failed tests and return nonzero if any tests fail. 16 * 17 * It will print the contents of the request and response buffers for each 18 * failing test. In a "fixed" version, all the tests should pass and there 19 * should be no output. 20 * 21 * In a "bleeding" version, you'll see: 22 * 23 * test_dtls1_heartbleed failed: 24 * expected payload len: 0 25 * received: 1024 26 * sent 26 characters 27 * "HEARTBLEED " 28 * received 1024 characters 29 * "HEARTBLEED \xde\xad\xbe\xef..." 30 * ** test_dtls1_heartbleed failed ** 31 * 32 * The contents of the returned buffer in the failing test will depend on the 33 * contents of memory on your machine. 34 * 35 * MORE INFORMATION 36 * ---------------- 37 * http://mike-bland.com/2014/04/12/heartbleed.html 38 * http://mike-bland.com/tags/heartbleed.html 39 */ 40 41#define OPENSSL_UNIT_TEST 42 43#include "../test/testutil.h" 44 45#include "../ssl/ssl_locl.h" 46#include <ctype.h> 47#include <stdio.h> 48#include <stdlib.h> 49#include <string.h> 50 51#if !defined(OPENSSL_NO_HEARTBEATS) && !defined(OPENSSL_NO_UNIT_TEST) 52 53/* As per https://tools.ietf.org/html/rfc6520#section-4 */ 54# define MIN_PADDING_SIZE 16 55 56/* Maximum number of payload characters to print as test output */ 57# define MAX_PRINTABLE_CHARACTERS 1024 58 59typedef struct heartbeat_test_fixture { 60 SSL_CTX *ctx; 61 SSL *s; 62 const char *test_case_name; 63 int (*process_heartbeat) (SSL *s); 64 unsigned char *payload; 65 int sent_payload_len; 66 int expected_return_value; 67 int return_payload_offset; 68 int expected_payload_len; 69 const char *expected_return_payload; 70} HEARTBEAT_TEST_FIXTURE; 71 72static HEARTBEAT_TEST_FIXTURE set_up(const char *const test_case_name, 73 const SSL_METHOD *meth) 74{ 75 HEARTBEAT_TEST_FIXTURE fixture; 76 int setup_ok = 1; 77 memset(&fixture, 0, sizeof(fixture)); 78 fixture.test_case_name = test_case_name; 79 80 fixture.ctx = SSL_CTX_new(meth); 81 if (!fixture.ctx) { 82 fprintf(stderr, "Failed to allocate SSL_CTX for test: %s\n", 83 test_case_name); 84 setup_ok = 0; 85 goto fail; 86 } 87 88 fixture.s = SSL_new(fixture.ctx); 89 if (!fixture.s) { 90 fprintf(stderr, "Failed to allocate SSL for test: %s\n", 91 test_case_name); 92 setup_ok = 0; 93 goto fail; 94 } 95 96 if (!ssl_init_wbio_buffer(fixture.s, 1)) { 97 fprintf(stderr, "Failed to set up wbio buffer for test: %s\n", 98 test_case_name); 99 setup_ok = 0; 100 goto fail; 101 } 102 103 if (!ssl3_setup_buffers(fixture.s)) { 104 fprintf(stderr, "Failed to setup buffers for test: %s\n", 105 test_case_name); 106 setup_ok = 0; 107 goto fail; 108 } 109 110 /* 111 * Clear the memory for the return buffer, since this isn't automatically 112 * zeroed in opt mode and will cause spurious test failures that will 113 * change with each execution. 114 */ 115 memset(fixture.s->s3->wbuf.buf, 0, fixture.s->s3->wbuf.len); 116 117 fail: 118 if (!setup_ok) { 119 ERR_print_errors_fp(stderr); 120 exit(EXIT_FAILURE); 121 } 122 return fixture; 123} 124 125static HEARTBEAT_TEST_FIXTURE set_up_dtls(const char *const test_case_name) 126{ 127 HEARTBEAT_TEST_FIXTURE fixture = set_up(test_case_name, 128 DTLSv1_server_method()); 129 fixture.process_heartbeat = dtls1_process_heartbeat; 130 131 /* 132 * As per dtls1_get_record(), skipping the following from the beginning 133 * of the returned heartbeat message: type-1 byte; version-2 bytes; 134 * sequence number-8 bytes; length-2 bytes And then skipping the 1-byte 135 * type encoded by process_heartbeat for a total of 14 bytes, at which 136 * point we can grab the length and the payload we seek. 137 */ 138 fixture.return_payload_offset = 14; 139 return fixture; 140} 141 142/* Needed by ssl3_write_bytes() */ 143static int dummy_handshake(SSL *s) 144{ 145 return 1; 146} 147 148static HEARTBEAT_TEST_FIXTURE set_up_tls(const char *const test_case_name) 149{ 150 HEARTBEAT_TEST_FIXTURE fixture = set_up(test_case_name, 151 TLSv1_server_method()); 152 fixture.process_heartbeat = tls1_process_heartbeat; 153 fixture.s->handshake_func = dummy_handshake; 154 155 /* 156 * As per do_ssl3_write(), skipping the following from the beginning of 157 * the returned heartbeat message: type-1 byte; version-2 bytes; length-2 158 * bytes And then skipping the 1-byte type encoded by process_heartbeat 159 * for a total of 6 bytes, at which point we can grab the length and the 160 * payload we seek. 161 */ 162 fixture.return_payload_offset = 6; 163 return fixture; 164} 165 166static void tear_down(HEARTBEAT_TEST_FIXTURE fixture) 167{ 168 ERR_print_errors_fp(stderr); 169 SSL_free(fixture.s); 170 SSL_CTX_free(fixture.ctx); 171} 172 173static void print_payload(const char *const prefix, 174 const unsigned char *payload, const int n) 175{ 176 const int end = n < MAX_PRINTABLE_CHARACTERS ? n 177 : MAX_PRINTABLE_CHARACTERS; 178 int i = 0; 179 180 printf("%s %d character%s", prefix, n, n == 1 ? "" : "s"); 181 if (end != n) 182 printf(" (first %d shown)", end); 183 printf("\n \""); 184 185 for (; i != end; ++i) { 186 const unsigned char c = payload[i]; 187 if (isprint(c)) 188 fputc(c, stdout); 189 else 190 printf("\\x%02x", c); 191 } 192 printf("\"\n"); 193} 194 195static int execute_heartbeat(HEARTBEAT_TEST_FIXTURE fixture) 196{ 197 int result = 0; 198 SSL *s = fixture.s; 199 unsigned char *payload = fixture.payload; 200 unsigned char sent_buf[MAX_PRINTABLE_CHARACTERS + 1]; 201 int return_value; 202 unsigned const char *p; 203 int actual_payload_len; 204 205 s->s3->rrec.data = payload; 206 s->s3->rrec.length = strlen((const char *)payload); 207 *payload++ = TLS1_HB_REQUEST; 208 s2n(fixture.sent_payload_len, payload); 209 210 /* 211 * Make a local copy of the request, since it gets overwritten at some 212 * point 213 */ 214 memcpy((char *)sent_buf, (const char *)payload, sizeof(sent_buf)); 215 216 return_value = fixture.process_heartbeat(s); 217 218 if (return_value != fixture.expected_return_value) { 219 printf("%s failed: expected return value %d, received %d\n", 220 fixture.test_case_name, fixture.expected_return_value, 221 return_value); 222 result = 1; 223 } 224 225 /* 226 * If there is any byte alignment, it will be stored in wbuf.offset. 227 */ 228 p = &(s->s3-> 229 wbuf.buf[fixture.return_payload_offset + s->s3->wbuf.offset]); 230 actual_payload_len = 0; 231 n2s(p, actual_payload_len); 232 233 if (actual_payload_len != fixture.expected_payload_len) { 234 printf("%s failed:\n expected payload len: %d\n received: %d\n", 235 fixture.test_case_name, fixture.expected_payload_len, 236 actual_payload_len); 237 print_payload("sent", sent_buf, strlen((const char *)sent_buf)); 238 print_payload("received", p, actual_payload_len); 239 result = 1; 240 } else { 241 char *actual_payload = 242 BUF_strndup((const char *)p, actual_payload_len); 243 if (strcmp(actual_payload, fixture.expected_return_payload) != 0) { 244 printf 245 ("%s failed:\n expected payload: \"%s\"\n received: \"%s\"\n", 246 fixture.test_case_name, fixture.expected_return_payload, 247 actual_payload); 248 result = 1; 249 } 250 OPENSSL_free(actual_payload); 251 } 252 253 if (result != 0) { 254 printf("** %s failed **\n--------\n", fixture.test_case_name); 255 } 256 return result; 257} 258 259static int honest_payload_size(unsigned char payload_buf[]) 260{ 261 /* Omit three-byte pad at the beginning for type and payload length */ 262 return strlen((const char *)&payload_buf[3]) - MIN_PADDING_SIZE; 263} 264 265# define SETUP_HEARTBEAT_TEST_FIXTURE(type)\ 266 SETUP_TEST_FIXTURE(HEARTBEAT_TEST_FIXTURE, set_up_##type) 267 268# define EXECUTE_HEARTBEAT_TEST()\ 269 EXECUTE_TEST(execute_heartbeat, tear_down) 270 271static int test_dtls1_not_bleeding() 272{ 273 SETUP_HEARTBEAT_TEST_FIXTURE(dtls); 274 /* Three-byte pad at the beginning for type and payload length */ 275 unsigned char payload_buf[MAX_PRINTABLE_CHARACTERS + 4] = 276 " Not bleeding, sixteen spaces of padding" " "; 277 const int payload_buf_len = honest_payload_size(payload_buf); 278 279 fixture.payload = &payload_buf[0]; 280 fixture.sent_payload_len = payload_buf_len; 281 fixture.expected_return_value = 0; 282 fixture.expected_payload_len = payload_buf_len; 283 fixture.expected_return_payload = 284 "Not bleeding, sixteen spaces of padding"; 285 EXECUTE_HEARTBEAT_TEST(); 286} 287 288static int test_dtls1_not_bleeding_empty_payload() 289{ 290 int payload_buf_len; 291 292 SETUP_HEARTBEAT_TEST_FIXTURE(dtls); 293 /* 294 * Three-byte pad at the beginning for type and payload length, plus a 295 * NUL at the end 296 */ 297 unsigned char payload_buf[4 + MAX_PRINTABLE_CHARACTERS]; 298 memset(payload_buf, ' ', MIN_PADDING_SIZE + 3); 299 payload_buf[MIN_PADDING_SIZE + 3] = '\0'; 300 payload_buf_len = honest_payload_size(payload_buf); 301 302 fixture.payload = &payload_buf[0]; 303 fixture.sent_payload_len = payload_buf_len; 304 fixture.expected_return_value = 0; 305 fixture.expected_payload_len = payload_buf_len; 306 fixture.expected_return_payload = ""; 307 EXECUTE_HEARTBEAT_TEST(); 308} 309 310static int test_dtls1_heartbleed() 311{ 312 SETUP_HEARTBEAT_TEST_FIXTURE(dtls); 313 /* Three-byte pad at the beginning for type and payload length */ 314 unsigned char payload_buf[4 + MAX_PRINTABLE_CHARACTERS] = 315 " HEARTBLEED "; 316 317 fixture.payload = &payload_buf[0]; 318 fixture.sent_payload_len = MAX_PRINTABLE_CHARACTERS; 319 fixture.expected_return_value = 0; 320 fixture.expected_payload_len = 0; 321 fixture.expected_return_payload = ""; 322 EXECUTE_HEARTBEAT_TEST(); 323} 324 325static int test_dtls1_heartbleed_empty_payload() 326{ 327 SETUP_HEARTBEAT_TEST_FIXTURE(dtls); 328 /* 329 * Excluding the NUL at the end, one byte short of type + payload length 330 * + minimum padding 331 */ 332 unsigned char payload_buf[MAX_PRINTABLE_CHARACTERS + 4]; 333 memset(payload_buf, ' ', MIN_PADDING_SIZE + 2); 334 payload_buf[MIN_PADDING_SIZE + 2] = '\0'; 335 336 fixture.payload = &payload_buf[0]; 337 fixture.sent_payload_len = MAX_PRINTABLE_CHARACTERS; 338 fixture.expected_return_value = 0; 339 fixture.expected_payload_len = 0; 340 fixture.expected_return_payload = ""; 341 EXECUTE_HEARTBEAT_TEST(); 342} 343 344static int test_dtls1_heartbleed_excessive_plaintext_length() 345{ 346 SETUP_HEARTBEAT_TEST_FIXTURE(dtls); 347 /* 348 * Excluding the NUL at the end, one byte in excess of maximum allowed 349 * heartbeat message length 350 */ 351 unsigned char payload_buf[SSL3_RT_MAX_PLAIN_LENGTH + 2]; 352 memset(payload_buf, ' ', sizeof(payload_buf)); 353 payload_buf[sizeof(payload_buf) - 1] = '\0'; 354 355 fixture.payload = &payload_buf[0]; 356 fixture.sent_payload_len = honest_payload_size(payload_buf); 357 fixture.expected_return_value = 0; 358 fixture.expected_payload_len = 0; 359 fixture.expected_return_payload = ""; 360 EXECUTE_HEARTBEAT_TEST(); 361} 362 363static int test_tls1_not_bleeding() 364{ 365 SETUP_HEARTBEAT_TEST_FIXTURE(tls); 366 /* Three-byte pad at the beginning for type and payload length */ 367 unsigned char payload_buf[MAX_PRINTABLE_CHARACTERS + 4] = 368 " Not bleeding, sixteen spaces of padding" " "; 369 const int payload_buf_len = honest_payload_size(payload_buf); 370 371 fixture.payload = &payload_buf[0]; 372 fixture.sent_payload_len = payload_buf_len; 373 fixture.expected_return_value = 0; 374 fixture.expected_payload_len = payload_buf_len; 375 fixture.expected_return_payload = 376 "Not bleeding, sixteen spaces of padding"; 377 EXECUTE_HEARTBEAT_TEST(); 378} 379 380static int test_tls1_not_bleeding_empty_payload() 381{ 382 int payload_buf_len; 383 384 SETUP_HEARTBEAT_TEST_FIXTURE(tls); 385 /* 386 * Three-byte pad at the beginning for type and payload length, plus a 387 * NUL at the end 388 */ 389 unsigned char payload_buf[4 + MAX_PRINTABLE_CHARACTERS]; 390 memset(payload_buf, ' ', MIN_PADDING_SIZE + 3); 391 payload_buf[MIN_PADDING_SIZE + 3] = '\0'; 392 payload_buf_len = honest_payload_size(payload_buf); 393 394 fixture.payload = &payload_buf[0]; 395 fixture.sent_payload_len = payload_buf_len; 396 fixture.expected_return_value = 0; 397 fixture.expected_payload_len = payload_buf_len; 398 fixture.expected_return_payload = ""; 399 EXECUTE_HEARTBEAT_TEST(); 400} 401 402static int test_tls1_heartbleed() 403{ 404 SETUP_HEARTBEAT_TEST_FIXTURE(tls); 405 /* Three-byte pad at the beginning for type and payload length */ 406 unsigned char payload_buf[MAX_PRINTABLE_CHARACTERS + 4] = 407 " HEARTBLEED "; 408 409 fixture.payload = &payload_buf[0]; 410 fixture.sent_payload_len = MAX_PRINTABLE_CHARACTERS; 411 fixture.expected_return_value = 0; 412 fixture.expected_payload_len = 0; 413 fixture.expected_return_payload = ""; 414 EXECUTE_HEARTBEAT_TEST(); 415} 416 417static int test_tls1_heartbleed_empty_payload() 418{ 419 SETUP_HEARTBEAT_TEST_FIXTURE(tls); 420 /* 421 * Excluding the NUL at the end, one byte short of type + payload length 422 * + minimum padding 423 */ 424 unsigned char payload_buf[MAX_PRINTABLE_CHARACTERS + 4]; 425 memset(payload_buf, ' ', MIN_PADDING_SIZE + 2); 426 payload_buf[MIN_PADDING_SIZE + 2] = '\0'; 427 428 fixture.payload = &payload_buf[0]; 429 fixture.sent_payload_len = MAX_PRINTABLE_CHARACTERS; 430 fixture.expected_return_value = 0; 431 fixture.expected_payload_len = 0; 432 fixture.expected_return_payload = ""; 433 EXECUTE_HEARTBEAT_TEST(); 434} 435 436# undef EXECUTE_HEARTBEAT_TEST 437# undef SETUP_HEARTBEAT_TEST_FIXTURE 438 439int main(int argc, char *argv[]) 440{ 441 int num_failed; 442 443 SSL_library_init(); 444 SSL_load_error_strings(); 445 446 num_failed = test_dtls1_not_bleeding() + 447 test_dtls1_not_bleeding_empty_payload() + 448 test_dtls1_heartbleed() + test_dtls1_heartbleed_empty_payload() + 449 /* 450 * The following test causes an assertion failure at 451 * ssl/d1_pkt.c:dtls1_write_bytes() in versions prior to 1.0.1g: 452 */ 453 (OPENSSL_VERSION_NUMBER >= 0x1000107fL ? 454 test_dtls1_heartbleed_excessive_plaintext_length() : 0) + 455 test_tls1_not_bleeding() + 456 test_tls1_not_bleeding_empty_payload() + 457 test_tls1_heartbleed() + test_tls1_heartbleed_empty_payload() + 0; 458 459 ERR_print_errors_fp(stderr); 460 461 if (num_failed != 0) { 462 printf("%d test%s failed\n", num_failed, num_failed != 1 ? "s" : ""); 463 return EXIT_FAILURE; 464 } 465 return EXIT_SUCCESS; 466} 467 468#else /* OPENSSL_NO_HEARTBEATS */ 469 470int main(int argc, char *argv[]) 471{ 472 return EXIT_SUCCESS; 473} 474#endif /* OPENSSL_NO_HEARTBEATS */ 475