heartbeat_test.c revision 280304
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[] = " Not bleeding, sixteen spaces of padding" 276 " "; 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 + MIN_PADDING_SIZE]; 298 memset(payload_buf, ' ', sizeof(payload_buf)); 299 payload_buf[sizeof(payload_buf) - 1] = '\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[] = " HEARTBLEED "; 315 316 fixture.payload = &payload_buf[0]; 317 fixture.sent_payload_len = MAX_PRINTABLE_CHARACTERS; 318 fixture.expected_return_value = 0; 319 fixture.expected_payload_len = 0; 320 fixture.expected_return_payload = ""; 321 EXECUTE_HEARTBEAT_TEST(); 322} 323 324static int test_dtls1_heartbleed_empty_payload() 325{ 326 SETUP_HEARTBEAT_TEST_FIXTURE(dtls); 327 /* 328 * Excluding the NUL at the end, one byte short of type + payload length 329 * + minimum padding 330 */ 331 unsigned char payload_buf[MIN_PADDING_SIZE + 3]; 332 memset(payload_buf, ' ', sizeof(payload_buf)); 333 payload_buf[sizeof(payload_buf) - 1] = '\0'; 334 335 fixture.payload = &payload_buf[0]; 336 fixture.sent_payload_len = MAX_PRINTABLE_CHARACTERS; 337 fixture.expected_return_value = 0; 338 fixture.expected_payload_len = 0; 339 fixture.expected_return_payload = ""; 340 EXECUTE_HEARTBEAT_TEST(); 341} 342 343static int test_dtls1_heartbleed_excessive_plaintext_length() 344{ 345 SETUP_HEARTBEAT_TEST_FIXTURE(dtls); 346 /* 347 * Excluding the NUL at the end, one byte in excess of maximum allowed 348 * heartbeat message length 349 */ 350 unsigned char payload_buf[SSL3_RT_MAX_PLAIN_LENGTH + 2]; 351 memset(payload_buf, ' ', sizeof(payload_buf)); 352 payload_buf[sizeof(payload_buf) - 1] = '\0'; 353 354 fixture.payload = &payload_buf[0]; 355 fixture.sent_payload_len = honest_payload_size(payload_buf); 356 fixture.expected_return_value = 0; 357 fixture.expected_payload_len = 0; 358 fixture.expected_return_payload = ""; 359 EXECUTE_HEARTBEAT_TEST(); 360} 361 362static int test_tls1_not_bleeding() 363{ 364 SETUP_HEARTBEAT_TEST_FIXTURE(tls); 365 /* Three-byte pad at the beginning for type and payload length */ 366 unsigned char payload_buf[] = " Not bleeding, sixteen spaces of padding" 367 " "; 368 const int payload_buf_len = honest_payload_size(payload_buf); 369 370 fixture.payload = &payload_buf[0]; 371 fixture.sent_payload_len = payload_buf_len; 372 fixture.expected_return_value = 0; 373 fixture.expected_payload_len = payload_buf_len; 374 fixture.expected_return_payload = 375 "Not bleeding, sixteen spaces of padding"; 376 EXECUTE_HEARTBEAT_TEST(); 377} 378 379static int test_tls1_not_bleeding_empty_payload() 380{ 381 int payload_buf_len; 382 383 SETUP_HEARTBEAT_TEST_FIXTURE(tls); 384 /* 385 * Three-byte pad at the beginning for type and payload length, plus a 386 * NUL at the end 387 */ 388 unsigned char payload_buf[4 + MIN_PADDING_SIZE]; 389 memset(payload_buf, ' ', sizeof(payload_buf)); 390 payload_buf[sizeof(payload_buf) - 1] = '\0'; 391 payload_buf_len = honest_payload_size(payload_buf); 392 393 fixture.payload = &payload_buf[0]; 394 fixture.sent_payload_len = payload_buf_len; 395 fixture.expected_return_value = 0; 396 fixture.expected_payload_len = payload_buf_len; 397 fixture.expected_return_payload = ""; 398 EXECUTE_HEARTBEAT_TEST(); 399} 400 401static int test_tls1_heartbleed() 402{ 403 SETUP_HEARTBEAT_TEST_FIXTURE(tls); 404 /* Three-byte pad at the beginning for type and payload length */ 405 unsigned char payload_buf[] = " HEARTBLEED "; 406 407 fixture.payload = &payload_buf[0]; 408 fixture.sent_payload_len = MAX_PRINTABLE_CHARACTERS; 409 fixture.expected_return_value = 0; 410 fixture.expected_payload_len = 0; 411 fixture.expected_return_payload = ""; 412 EXECUTE_HEARTBEAT_TEST(); 413} 414 415static int test_tls1_heartbleed_empty_payload() 416{ 417 SETUP_HEARTBEAT_TEST_FIXTURE(tls); 418 /* 419 * Excluding the NUL at the end, one byte short of type + payload length 420 * + minimum padding 421 */ 422 unsigned char payload_buf[MIN_PADDING_SIZE + 3]; 423 memset(payload_buf, ' ', sizeof(payload_buf)); 424 payload_buf[sizeof(payload_buf) - 1] = '\0'; 425 426 fixture.payload = &payload_buf[0]; 427 fixture.sent_payload_len = MAX_PRINTABLE_CHARACTERS; 428 fixture.expected_return_value = 0; 429 fixture.expected_payload_len = 0; 430 fixture.expected_return_payload = ""; 431 EXECUTE_HEARTBEAT_TEST(); 432} 433 434# undef EXECUTE_HEARTBEAT_TEST 435# undef SETUP_HEARTBEAT_TEST_FIXTURE 436 437int main(int argc, char *argv[]) 438{ 439 int num_failed; 440 441 SSL_library_init(); 442 SSL_load_error_strings(); 443 444 num_failed = test_dtls1_not_bleeding() + 445 test_dtls1_not_bleeding_empty_payload() + 446 test_dtls1_heartbleed() + test_dtls1_heartbleed_empty_payload() + 447 /* 448 * The following test causes an assertion failure at 449 * ssl/d1_pkt.c:dtls1_write_bytes() in versions prior to 1.0.1g: 450 */ 451 (OPENSSL_VERSION_NUMBER >= 0x1000107fL ? 452 test_dtls1_heartbleed_excessive_plaintext_length() : 0) + 453 test_tls1_not_bleeding() + 454 test_tls1_not_bleeding_empty_payload() + 455 test_tls1_heartbleed() + test_tls1_heartbleed_empty_payload() + 0; 456 457 ERR_print_errors_fp(stderr); 458 459 if (num_failed != 0) { 460 printf("%d test%s failed\n", num_failed, num_failed != 1 ? "s" : ""); 461 return EXIT_FAILURE; 462 } 463 return EXIT_SUCCESS; 464} 465 466#else /* OPENSSL_NO_HEARTBEATS */ 467 468int main(int argc, char *argv[]) 469{ 470 return EXIT_SUCCESS; 471} 472#endif /* OPENSSL_NO_HEARTBEATS */ 473