70-test_sslrecords.t 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. #! /usr/bin/env perl
  2. # Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
  3. #
  4. # Licensed under the OpenSSL license (the "License"). You may not use
  5. # this file except in compliance with the License. You can obtain a copy
  6. # in the file LICENSE in the source distribution or at
  7. # https://www.openssl.org/source/license.html
  8. use strict;
  9. use feature 'state';
  10. use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/;
  11. use OpenSSL::Test::Utils;
  12. use TLSProxy::Proxy;
  13. my $test_name = "test_sslrecords";
  14. setup($test_name);
  15. plan skip_all => "TLSProxy isn't usable on $^O"
  16. if $^O =~ /^(VMS)$/;
  17. plan skip_all => "$test_name needs the dynamic engine feature enabled"
  18. if disabled("engine") || disabled("dynamic-engine");
  19. plan skip_all => "$test_name needs the sock feature enabled"
  20. if disabled("sock");
  21. plan skip_all => "$test_name needs TLSv1.2 enabled"
  22. if disabled("tls1_2");
  23. $ENV{OPENSSL_ia32cap} = '~0x200000200000000';
  24. my $proxy = TLSProxy::Proxy->new(
  25. \&add_empty_recs_filter,
  26. cmdstr(app(["openssl"]), display => 1),
  27. srctop_file("apps", "server.pem"),
  28. (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
  29. );
  30. my $boundary_test_type;
  31. my $fatal_alert = 0; # set by filters at expected fatal alerts
  32. #Test 1: Injecting out of context empty records should fail
  33. my $content_type = TLSProxy::Record::RT_APPLICATION_DATA;
  34. my $inject_recs_num = 1;
  35. $proxy->serverflags("-tls1_2");
  36. $proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
  37. plan tests => 18;
  38. ok($fatal_alert, "Out of context empty records test");
  39. #Test 2: Injecting in context empty records should succeed
  40. $proxy->clear();
  41. $content_type = TLSProxy::Record::RT_HANDSHAKE;
  42. $proxy->serverflags("-tls1_2");
  43. $proxy->start();
  44. ok(TLSProxy::Message->success(), "In context empty records test");
  45. #Test 3: Injecting too many in context empty records should fail
  46. $fatal_alert = 0;
  47. $proxy->clear();
  48. #We allow 32 consecutive in context empty records
  49. $inject_recs_num = 33;
  50. $proxy->serverflags("-tls1_2");
  51. $proxy->start();
  52. ok($fatal_alert, "Too many in context empty records test");
  53. #Test 4: Injecting a fragmented fatal alert should fail. We expect the server to
  54. # send back an alert of its own because it cannot handle fragmented
  55. # alerts
  56. $fatal_alert = 0;
  57. $proxy->clear();
  58. $proxy->filter(\&add_frag_alert_filter);
  59. $proxy->serverflags("-tls1_2");
  60. $proxy->start();
  61. ok($fatal_alert, "Fragmented alert records test");
  62. #Run some SSLv2 ClientHello tests
  63. use constant {
  64. TLSV1_2_IN_SSLV2 => 0,
  65. SSLV2_IN_SSLV2 => 1,
  66. FRAGMENTED_IN_TLSV1_2 => 2,
  67. FRAGMENTED_IN_SSLV2 => 3,
  68. ALERT_BEFORE_SSLV2 => 4
  69. };
  70. #Test 5: Inject an SSLv2 style record format for a TLSv1.2 ClientHello
  71. my $sslv2testtype = TLSV1_2_IN_SSLV2;
  72. $proxy->clear();
  73. $proxy->filter(\&add_sslv2_filter);
  74. $proxy->serverflags("-tls1_2");
  75. $proxy->start();
  76. ok(TLSProxy::Message->success(), "TLSv1.2 in SSLv2 ClientHello test");
  77. #Test 6: Inject an SSLv2 style record format for an SSLv2 ClientHello. We don't
  78. # support this so it should fail. We actually treat it as an unknown
  79. # protocol so we don't even send an alert in this case.
  80. $sslv2testtype = SSLV2_IN_SSLV2;
  81. $proxy->clear();
  82. $proxy->serverflags("-tls1_2");
  83. $proxy->start();
  84. ok(TLSProxy::Message->fail(), "SSLv2 in SSLv2 ClientHello test");
  85. #Test 7: Sanity check ClientHello fragmentation. This isn't really an SSLv2 test
  86. # at all, but it gives us confidence that Test 8 fails for the right
  87. # reasons
  88. $sslv2testtype = FRAGMENTED_IN_TLSV1_2;
  89. $proxy->clear();
  90. $proxy->serverflags("-tls1_2");
  91. $proxy->start();
  92. ok(TLSProxy::Message->success(), "Fragmented ClientHello in TLSv1.2 test");
  93. #Test 8: Fragment a TLSv1.2 ClientHello across a TLS1.2 record; an SSLv2
  94. # record; and another TLS1.2 record. This isn't allowed so should fail
  95. $sslv2testtype = FRAGMENTED_IN_SSLV2;
  96. $proxy->clear();
  97. $proxy->serverflags("-tls1_2");
  98. $proxy->start();
  99. ok(TLSProxy::Message->fail(), "Fragmented ClientHello in TLSv1.2/SSLv2 test");
  100. #Test 9: Send a TLS warning alert before an SSLv2 ClientHello. This should
  101. # fail because an SSLv2 ClientHello must be the first record.
  102. $sslv2testtype = ALERT_BEFORE_SSLV2;
  103. $proxy->clear();
  104. $proxy->serverflags("-tls1_2");
  105. $proxy->start();
  106. ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test");
  107. #Unrecognised record type tests
  108. #Test 10: Sending an unrecognised record type in TLS1.2 should fail
  109. $fatal_alert = 0;
  110. $proxy->clear();
  111. $proxy->serverflags("-tls1_2");
  112. $proxy->filter(\&add_unknown_record_type);
  113. $proxy->start();
  114. ok($fatal_alert, "Unrecognised record type in TLS1.2");
  115. SKIP: {
  116. skip "TLSv1.1 disabled", 1 if disabled("tls1_1");
  117. #Test 11: Sending an unrecognised record type in TLS1.1 should fail
  118. $fatal_alert = 0;
  119. $proxy->clear();
  120. $proxy->clientflags("-tls1_1");
  121. $proxy->start();
  122. ok($fatal_alert, "Unrecognised record type in TLS1.1");
  123. }
  124. #Test 12: Sending a different record version in TLS1.2 should fail
  125. $fatal_alert = 0;
  126. $proxy->clear();
  127. $proxy->clientflags("-tls1_2");
  128. $proxy->filter(\&change_version);
  129. $proxy->start();
  130. ok($fatal_alert, "Changed record version in TLS1.2");
  131. #TLS1.3 specific tests
  132. SKIP: {
  133. skip "TLSv1.3 disabled", 6 if disabled("tls1_3");
  134. #Test 13: Sending a different record version in TLS1.3 should fail
  135. $proxy->clear();
  136. $proxy->filter(\&change_version);
  137. $proxy->start();
  138. ok(TLSProxy::Message->fail(), "Changed record version in TLS1.3");
  139. #Test 14: Sending an unrecognised record type in TLS1.3 should fail
  140. $fatal_alert = 0;
  141. $proxy->clear();
  142. $proxy->filter(\&add_unknown_record_type);
  143. $proxy->start();
  144. ok($fatal_alert, "Unrecognised record type in TLS1.3");
  145. #Test 15: Sending an outer record type other than app data once encrypted
  146. #should fail
  147. $fatal_alert = 0;
  148. $proxy->clear();
  149. $proxy->filter(\&change_outer_record_type);
  150. $proxy->start();
  151. ok($fatal_alert, "Wrong outer record type in TLS1.3");
  152. use constant {
  153. DATA_AFTER_SERVER_HELLO => 0,
  154. DATA_AFTER_FINISHED => 1,
  155. DATA_AFTER_KEY_UPDATE => 2
  156. };
  157. #Test 16: Sending a ServerHello which doesn't end on a record boundary
  158. # should fail
  159. $fatal_alert = 0;
  160. $proxy->clear();
  161. $boundary_test_type = DATA_AFTER_SERVER_HELLO;
  162. $proxy->filter(\&not_on_record_boundary);
  163. $proxy->start();
  164. ok($fatal_alert, "Record not on boundary in TLS1.3 (ServerHello)");
  165. #Test 17: Sending a Finished which doesn't end on a record boundary
  166. # should fail
  167. $fatal_alert = 0;
  168. $proxy->clear();
  169. $boundary_test_type = DATA_AFTER_FINISHED;
  170. $proxy->filter(\&not_on_record_boundary);
  171. $proxy->start();
  172. ok($fatal_alert, "Record not on boundary in TLS1.3 (Finished)");
  173. #Test 18: Sending a KeyUpdate which doesn't end on a record boundary
  174. # should fail
  175. $fatal_alert = 0;
  176. $proxy->clear();
  177. $boundary_test_type = DATA_AFTER_KEY_UPDATE;
  178. $proxy->filter(\&not_on_record_boundary);
  179. $proxy->start();
  180. ok($fatal_alert, "Record not on boundary in TLS1.3 (KeyUpdate)");
  181. }
  182. sub add_empty_recs_filter
  183. {
  184. my $proxy = shift;
  185. my $records = $proxy->record_list;
  186. # We're only interested in the initial ClientHello
  187. if ($proxy->flight != 0) {
  188. $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == 10;
  189. return;
  190. }
  191. for (my $i = 0; $i < $inject_recs_num; $i++) {
  192. my $record = TLSProxy::Record->new(
  193. 0,
  194. $content_type,
  195. TLSProxy::Record::VERS_TLS_1_2,
  196. 0,
  197. 0,
  198. 0,
  199. 0,
  200. "",
  201. ""
  202. );
  203. push @{$records}, $record;
  204. }
  205. }
  206. sub add_frag_alert_filter
  207. {
  208. my $proxy = shift;
  209. my $records = $proxy->record_list;
  210. my $byte;
  211. # We're only interested in the initial ClientHello
  212. if ($proxy->flight != 0) {
  213. $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(1) == 10;
  214. return;
  215. }
  216. # Add a zero length fragment first
  217. #my $record = TLSProxy::Record->new(
  218. # 0,
  219. # TLSProxy::Record::RT_ALERT,
  220. # TLSProxy::Record::VERS_TLS_1_2,
  221. # 0,
  222. # 0,
  223. # 0,
  224. # "",
  225. # ""
  226. #);
  227. #push @{$proxy->record_list}, $record;
  228. # Now add the alert level (Fatal) as a separate record
  229. $byte = pack('C', TLSProxy::Message::AL_LEVEL_FATAL);
  230. my $record = TLSProxy::Record->new(
  231. 0,
  232. TLSProxy::Record::RT_ALERT,
  233. TLSProxy::Record::VERS_TLS_1_2,
  234. 1,
  235. 0,
  236. 1,
  237. 1,
  238. $byte,
  239. $byte
  240. );
  241. push @{$records}, $record;
  242. # And finally the description (Unexpected message) in a third record
  243. $byte = pack('C', TLSProxy::Message::AL_DESC_UNEXPECTED_MESSAGE);
  244. $record = TLSProxy::Record->new(
  245. 0,
  246. TLSProxy::Record::RT_ALERT,
  247. TLSProxy::Record::VERS_TLS_1_2,
  248. 1,
  249. 0,
  250. 1,
  251. 1,
  252. $byte,
  253. $byte
  254. );
  255. push @{$records}, $record;
  256. }
  257. sub add_sslv2_filter
  258. {
  259. my $proxy = shift;
  260. my $clienthello;
  261. my $record;
  262. # We're only interested in the initial ClientHello
  263. if ($proxy->flight != 0) {
  264. return;
  265. }
  266. # Ditch the real ClientHello - we're going to replace it with our own
  267. shift @{$proxy->record_list};
  268. if ($sslv2testtype == ALERT_BEFORE_SSLV2) {
  269. my $alert = pack('CC', TLSProxy::Message::AL_LEVEL_FATAL,
  270. TLSProxy::Message::AL_DESC_NO_RENEGOTIATION);
  271. my $alertlen = length $alert;
  272. $record = TLSProxy::Record->new(
  273. 0,
  274. TLSProxy::Record::RT_ALERT,
  275. TLSProxy::Record::VERS_TLS_1_2,
  276. $alertlen,
  277. 0,
  278. $alertlen,
  279. $alertlen,
  280. $alert,
  281. $alert
  282. );
  283. push @{$proxy->record_list}, $record;
  284. }
  285. if ($sslv2testtype == ALERT_BEFORE_SSLV2
  286. || $sslv2testtype == TLSV1_2_IN_SSLV2
  287. || $sslv2testtype == SSLV2_IN_SSLV2) {
  288. # This is an SSLv2 format ClientHello
  289. $clienthello =
  290. pack "C44",
  291. 0x01, # ClientHello
  292. 0x03, 0x03, #TLSv1.2
  293. 0x00, 0x03, # Ciphersuites len
  294. 0x00, 0x00, # Session id len
  295. 0x00, 0x20, # Challenge len
  296. 0x00, 0x00, 0x2f, #AES128-SHA
  297. 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
  298. 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
  299. 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6; # Challenge
  300. if ($sslv2testtype == SSLV2_IN_SSLV2) {
  301. # Set the version to "real" SSLv2
  302. vec($clienthello, 1, 8) = 0x00;
  303. vec($clienthello, 2, 8) = 0x02;
  304. }
  305. my $chlen = length $clienthello;
  306. $record = TLSProxy::Record->new(
  307. 0,
  308. TLSProxy::Record::RT_HANDSHAKE,
  309. TLSProxy::Record::VERS_TLS_1_2,
  310. $chlen,
  311. 1, #SSLv2
  312. $chlen,
  313. $chlen,
  314. $clienthello,
  315. $clienthello
  316. );
  317. push @{$proxy->record_list}, $record;
  318. } else {
  319. # For this test we're using a real TLS ClientHello
  320. $clienthello =
  321. pack "C49",
  322. 0x01, # ClientHello
  323. 0x00, 0x00, 0x2D, # Message length
  324. 0x03, 0x03, # TLSv1.2
  325. 0x01, 0x18, 0x9F, 0x76, 0xEC, 0x57, 0xCE, 0xE5, 0xB3, 0xAB, 0x79, 0x90,
  326. 0xAD, 0xAC, 0x6E, 0xD1, 0x58, 0x35, 0x03, 0x97, 0x16, 0x10, 0x82, 0x56,
  327. 0xD8, 0x55, 0xFF, 0xE1, 0x8A, 0xA3, 0x2E, 0xF6, # Random
  328. 0x00, # Session id len
  329. 0x00, 0x04, # Ciphersuites len
  330. 0x00, 0x2f, # AES128-SHA
  331. 0x00, 0xff, # Empty reneg info SCSV
  332. 0x01, # Compression methods len
  333. 0x00, # Null compression
  334. 0x00, 0x00; # Extensions len
  335. # Split this into 3: A TLS record; a SSLv2 record and a TLS record.
  336. # We deliberately split the second record prior to the Challenge/Random
  337. # and set the first byte of the random to 1. This makes the second SSLv2
  338. # record look like an SSLv2 ClientHello
  339. my $frag1 = substr $clienthello, 0, 6;
  340. my $frag2 = substr $clienthello, 6, 32;
  341. my $frag3 = substr $clienthello, 38;
  342. my $fraglen = length $frag1;
  343. $record = TLSProxy::Record->new(
  344. 0,
  345. TLSProxy::Record::RT_HANDSHAKE,
  346. TLSProxy::Record::VERS_TLS_1_2,
  347. $fraglen,
  348. 0,
  349. $fraglen,
  350. $fraglen,
  351. $frag1,
  352. $frag1
  353. );
  354. push @{$proxy->record_list}, $record;
  355. $fraglen = length $frag2;
  356. my $recvers;
  357. if ($sslv2testtype == FRAGMENTED_IN_SSLV2) {
  358. $recvers = 1;
  359. } else {
  360. $recvers = 0;
  361. }
  362. $record = TLSProxy::Record->new(
  363. 0,
  364. TLSProxy::Record::RT_HANDSHAKE,
  365. TLSProxy::Record::VERS_TLS_1_2,
  366. $fraglen,
  367. $recvers,
  368. $fraglen,
  369. $fraglen,
  370. $frag2,
  371. $frag2
  372. );
  373. push @{$proxy->record_list}, $record;
  374. $fraglen = length $frag3;
  375. $record = TLSProxy::Record->new(
  376. 0,
  377. TLSProxy::Record::RT_HANDSHAKE,
  378. TLSProxy::Record::VERS_TLS_1_2,
  379. $fraglen,
  380. 0,
  381. $fraglen,
  382. $fraglen,
  383. $frag3,
  384. $frag3
  385. );
  386. push @{$proxy->record_list}, $record;
  387. }
  388. }
  389. sub add_unknown_record_type
  390. {
  391. my $proxy = shift;
  392. my $records = $proxy->record_list;
  393. state $added_record;
  394. # We'll change a record after the initial version neg has taken place
  395. if ($proxy->flight == 0) {
  396. $added_record = 0;
  397. return;
  398. } elsif ($proxy->flight != 1 || $added_record) {
  399. $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 10;
  400. return;
  401. }
  402. my $record = TLSProxy::Record->new(
  403. 1,
  404. TLSProxy::Record::RT_UNKNOWN,
  405. @{$records}[-1]->version(),
  406. 1,
  407. 0,
  408. 1,
  409. 1,
  410. "X",
  411. "X"
  412. );
  413. #Find ServerHello record and insert after that
  414. my $i;
  415. for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
  416. next;
  417. }
  418. $i++;
  419. splice @{$proxy->record_list}, $i, 0, $record;
  420. $added_record = 1;
  421. }
  422. sub change_version
  423. {
  424. my $proxy = shift;
  425. my $records = $proxy->record_list;
  426. # We'll change a version after the initial version neg has taken place
  427. if ($proxy->flight != 1) {
  428. $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 70;
  429. return;
  430. }
  431. if ($#{$records} > 1) {
  432. # ... typically in ServerHelloDone
  433. @{$records}[-1]->version(TLSProxy::Record::VERS_TLS_1_1);
  434. }
  435. }
  436. sub change_outer_record_type
  437. {
  438. my $proxy = shift;
  439. my $records = $proxy->record_list;
  440. # We'll change a record after the initial version neg has taken place
  441. if ($proxy->flight != 1) {
  442. $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 10;
  443. return;
  444. }
  445. # Find CCS record and change record after that
  446. my $i = 0;
  447. foreach my $record (@{$records}) {
  448. last if $record->content_type == TLSProxy::Record::RT_CCS;
  449. $i++;
  450. }
  451. if (defined(${$records}[++$i])) {
  452. ${$records}[$i]->outer_content_type(TLSProxy::Record::RT_HANDSHAKE);
  453. }
  454. }
  455. sub not_on_record_boundary
  456. {
  457. my $proxy = shift;
  458. my $records = $proxy->record_list;
  459. my $data;
  460. #Find server's first flight
  461. if ($proxy->flight != 1) {
  462. $fatal_alert = 1 if @{$records}[-1]->is_fatal_alert(0) == 10;
  463. return;
  464. }
  465. if ($boundary_test_type == DATA_AFTER_SERVER_HELLO) {
  466. #Merge the ServerHello and EncryptedExtensions records into one
  467. my $i = 0;
  468. foreach my $record (@{$records}) {
  469. if ($record->content_type == TLSProxy::Record::RT_HANDSHAKE) {
  470. $record->{sent} = 1; # pretend it's sent already
  471. last;
  472. }
  473. $i++;
  474. }
  475. if (defined(${$records}[$i+1])) {
  476. $data = ${$records}[$i]->data();
  477. $data .= ${$records}[$i+1]->decrypt_data();
  478. ${$records}[$i+1]->data($data);
  479. ${$records}[$i+1]->len(length $data);
  480. #Delete the old ServerHello record
  481. splice @{$records}, $i, 1;
  482. }
  483. } elsif ($boundary_test_type == DATA_AFTER_FINISHED) {
  484. return if @{$proxy->{message_list}}[-1]->{mt}
  485. != TLSProxy::Message::MT_FINISHED;
  486. my $last_record = @{$records}[-1];
  487. $data = $last_record->decrypt_data;
  488. #Add a KeyUpdate message onto the end of the Finished record
  489. my $keyupdate = pack "C5",
  490. 0x18, # KeyUpdate
  491. 0x00, 0x00, 0x01, # Message length
  492. 0x00; # Update not requested
  493. $data .= $keyupdate;
  494. #Add content type and tag
  495. $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
  496. #Update the record
  497. $last_record->data($data);
  498. $last_record->len(length $data);
  499. } else {
  500. return if @{$proxy->{message_list}}[-1]->{mt}
  501. != TLSProxy::Message::MT_FINISHED;
  502. #KeyUpdates must end on a record boundary
  503. my $record = TLSProxy::Record->new(
  504. 1,
  505. TLSProxy::Record::RT_APPLICATION_DATA,
  506. TLSProxy::Record::VERS_TLS_1_2,
  507. 0,
  508. 0,
  509. 0,
  510. 0,
  511. "",
  512. ""
  513. );
  514. #Add two KeyUpdate messages into a single record
  515. my $keyupdate = pack "C5",
  516. 0x18, # KeyUpdate
  517. 0x00, 0x00, 0x01, # Message length
  518. 0x00; # Update not requested
  519. $data = $keyupdate.$keyupdate;
  520. #Add content type and tag
  521. $data .= pack("C", TLSProxy::Record::RT_HANDSHAKE).("\0"x16);
  522. $record->data($data);
  523. $record->len(length $data);
  524. push @{$records}, $record;
  525. }
  526. }