1#! /usr/bin/env perl 2# Copyright 2015-2021 The OpenSSL Project Authors. All Rights Reserved. 3# 4# Licensed under the Apache License 2.0 (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 9use strict; 10use feature 'state'; 11 12use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/; 13use OpenSSL::Test::Utils; 14use TLSProxy::Proxy; 15 16my $test_name = "test_sslextension"; 17setup($test_name); 18 19plan skip_all => "TLSProxy isn't usable on $^O" 20 if $^O =~ /^(VMS)$/; 21 22plan skip_all => "$test_name needs the dynamic engine feature enabled" 23 if disabled("engine") || disabled("dynamic-engine"); 24 25plan skip_all => "$test_name needs the sock feature enabled" 26 if disabled("sock"); 27 28plan skip_all => "$test_name needs TLS enabled" 29 if alldisabled(available_protocols("tls")); 30 31my $no_below_tls13 = alldisabled(("tls1", "tls1_1", "tls1_2")) 32 || (!disabled("tls1_3") && disabled("tls1_2")); 33 34use constant { 35 UNSOLICITED_SERVER_NAME => 0, 36 UNSOLICITED_SERVER_NAME_TLS13 => 1, 37 UNSOLICITED_SCT => 2, 38 NONCOMPLIANT_SUPPORTED_GROUPS => 3 39}; 40 41my $testtype; 42my $fatal_alert = 0; # set by filter on fatal alert 43 44$ENV{OPENSSL_ia32cap} = '~0x200000200000000'; 45my $proxy = TLSProxy::Proxy->new( 46 \&inject_duplicate_extension_clienthello, 47 cmdstr(app(["openssl"]), display => 1), 48 srctop_file("apps", "server.pem"), 49 (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE}) 50); 51 52 53sub extension_filter 54{ 55 my $proxy = shift; 56 57 if ($proxy->flight == 1) { 58 # Change the ServerRandom so that the downgrade sentinel doesn't cause 59 # the connection to fail 60 my $message = ${$proxy->message_list}[1]; 61 $message->random("\0"x32); 62 $message->repack(); 63 return; 64 } 65 66 # We're only interested in the initial ClientHello 67 if ($proxy->flight != 0) { 68 return; 69 } 70 71 foreach my $message (@{$proxy->message_list}) { 72 if ($message->mt == TLSProxy::Message::MT_CLIENT_HELLO) { 73 # Remove all extensions and set the extension len to zero 74 $message->extension_data({}); 75 $message->extensions_len(0); 76 # Extensions have been removed so make sure we don't try to use them 77 $message->process_extensions(); 78 79 $message->repack(); 80 } 81 } 82} 83 84sub inject_duplicate_extension 85{ 86 my ($proxy, $message_type) = @_; 87 88 foreach my $message (@{$proxy->message_list}) { 89 if ($message->mt == $message_type) { 90 my %extensions = %{$message->extension_data}; 91 # Add a duplicate extension. We use cryptopro_bug since we never 92 # normally write that one, and it is allowed as unsolicited in the 93 # ServerHello 94 $message->set_extension(TLSProxy::Message::EXT_CRYPTOPRO_BUG_EXTENSION, ""); 95 $message->dupext(TLSProxy::Message::EXT_CRYPTOPRO_BUG_EXTENSION); 96 $message->repack(); 97 } 98 } 99} 100 101sub inject_duplicate_extension_clienthello 102{ 103 my $proxy = shift; 104 105 # We're only interested in the initial ClientHello 106 if ($proxy->flight == 0) { 107 inject_duplicate_extension($proxy, TLSProxy::Message::MT_CLIENT_HELLO); 108 return; 109 } 110 111 my $last_record = @{$proxy->{record_list}}[-1]; 112 $fatal_alert = 1 if $last_record->is_fatal_alert(1); 113} 114 115sub inject_duplicate_extension_serverhello 116{ 117 my $proxy = shift; 118 119 # We're only interested in the initial ServerHello 120 if ($proxy->flight == 0) { 121 return; 122 } elsif ($proxy->flight == 1) { 123 inject_duplicate_extension($proxy, TLSProxy::Message::MT_SERVER_HELLO); 124 return; 125 } 126 127 my $last_record = @{$proxy->{record_list}}[-1]; 128 $fatal_alert = 1 if $last_record->is_fatal_alert(0); 129} 130 131sub inject_unsolicited_extension 132{ 133 my $proxy = shift; 134 my $message; 135 state $sent_unsolisited_extension; 136 137 if ($proxy->flight == 0) { 138 $sent_unsolisited_extension = 0; 139 return; 140 } 141 142 # We're only interested in the initial ServerHello/EncryptedExtensions 143 if ($proxy->flight != 1) { 144 if ($sent_unsolisited_extension) { 145 my $last_record = @{$proxy->record_list}[-1]; 146 $fatal_alert = 1 if $last_record->is_fatal_alert(0); 147 } 148 return; 149 } 150 151 if ($testtype == UNSOLICITED_SERVER_NAME_TLS13) { 152 return if (!defined($message = ${$proxy->message_list}[2])); 153 die "Expecting EE message ".($message->mt)."," 154 .${$proxy->message_list}[1]->mt.", " 155 .${$proxy->message_list}[3]->mt 156 if $message->mt != TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS; 157 } else { 158 $message = ${$proxy->message_list}[1]; 159 } 160 161 my $ext = pack "C2", 162 0x00, 0x00; #Extension length 163 164 my $type; 165 if ($testtype == UNSOLICITED_SERVER_NAME 166 || $testtype == UNSOLICITED_SERVER_NAME_TLS13) { 167 $type = TLSProxy::Message::EXT_SERVER_NAME; 168 } elsif ($testtype == UNSOLICITED_SCT) { 169 $type = TLSProxy::Message::EXT_SCT; 170 } elsif ($testtype == NONCOMPLIANT_SUPPORTED_GROUPS) { 171 $type = TLSProxy::Message::EXT_SUPPORTED_GROUPS; 172 } 173 $message->set_extension($type, $ext); 174 $message->repack(); 175 $sent_unsolisited_extension = 1; 176} 177 178sub inject_cryptopro_extension 179{ 180 my $proxy = shift; 181 182 # We're only interested in the initial ClientHello 183 if ($proxy->flight != 0) { 184 return; 185 } 186 187 my $message = ${$proxy->message_list}[0]; 188 $message->set_extension(TLSProxy::Message::EXT_CRYPTOPRO_BUG_EXTENSION, ""); 189 $message->repack(); 190} 191 192# Test 1-2: Sending a duplicate extension should fail. 193$proxy->start() or plan skip_all => "Unable to start up Proxy for tests"; 194plan tests => 8; 195ok($fatal_alert, "Duplicate ClientHello extension"); 196 197SKIP: { 198 skip "TLS <= 1.2 disabled", 4 if $no_below_tls13; 199 200 $fatal_alert = 0; 201 $proxy->clear(); 202 $proxy->filter(\&inject_duplicate_extension_serverhello); 203 $proxy->clientflags("-no_tls1_3"); 204 $proxy->start(); 205 ok($fatal_alert, "Duplicate ServerHello extension"); 206 207 #Test 3: Sending a zero length extension block should pass 208 $proxy->clear(); 209 $proxy->filter(\&extension_filter); 210 $proxy->ciphers("AES128-SHA:\@SECLEVEL=0"); 211 $proxy->clientflags("-no_tls1_3"); 212 $proxy->start(); 213 ok(TLSProxy::Message->success, "Zero extension length test"); 214 215 #Test 4: Inject an unsolicited extension (<= TLSv1.2) 216 $fatal_alert = 0; 217 $proxy->clear(); 218 $proxy->filter(\&inject_unsolicited_extension); 219 $testtype = UNSOLICITED_SERVER_NAME; 220 $proxy->clientflags("-no_tls1_3 -noservername"); 221 $proxy->start(); 222 ok($fatal_alert, "Unsolicited server name extension"); 223 224 #Test 5: Send the cryptopro extension in a ClientHello. Normally this is an 225 # unsolicited extension only ever seen in the ServerHello. We should 226 # ignore it in a ClientHello 227 $proxy->clear(); 228 $proxy->filter(\&inject_cryptopro_extension); 229 $proxy->clientflags("-no_tls1_3"); 230 $proxy->start(); 231 ok(TLSProxy::Message->success(), "Cryptopro extension in ClientHello"); 232} 233 234SKIP: { 235 skip "TLS <= 1.2 disabled or EC disabled", 1 236 if $no_below_tls13 || disabled("ec"); 237 #Test 6: Inject a noncompliant supported_groups extension (<= TLSv1.2) 238 $proxy->clear(); 239 $proxy->filter(\&inject_unsolicited_extension); 240 $testtype = NONCOMPLIANT_SUPPORTED_GROUPS; 241 $proxy->clientflags("-no_tls1_3"); 242 $proxy->start(); 243 ok(TLSProxy::Message->success(), "Noncompliant supported_groups extension"); 244} 245 246SKIP: { 247 skip "TLS <= 1.2 or CT disabled", 1 248 if $no_below_tls13 || disabled("ct"); 249 #Test 7: Same as above for the SCT extension which has special handling 250 $fatal_alert = 0; 251 $proxy->clear(); 252 $proxy->filter(\&inject_unsolicited_extension); 253 $testtype = UNSOLICITED_SCT; 254 $proxy->clientflags("-no_tls1_3"); 255 $proxy->start(); 256 ok($fatal_alert, "Unsolicited sct extension"); 257} 258 259SKIP: { 260 skip "TLS 1.3 disabled", 1 261 if disabled("tls1_3") || (disabled("ec") && disabled("dh")); 262 #Test 8: Inject an unsolicited extension (TLSv1.3) 263 $fatal_alert = 0; 264 $proxy->clear(); 265 $proxy->filter(\&inject_unsolicited_extension); 266 $testtype = UNSOLICITED_SERVER_NAME_TLS13; 267 $proxy->clientflags("-noservername"); 268 $proxy->start(); 269 ok($fatal_alert, "Unsolicited server name extension (TLSv1.3)"); 270} 271