70-test_sslrecords.t 16 KB

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