1#! /usr/bin/env perl 2# Copyright 2015-2024 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 44my $proxy = TLSProxy::Proxy->new( 45 \&inject_duplicate_extension_clienthello, 46 cmdstr(app(["openssl"]), display => 1), 47 srctop_file("apps", "server.pem"), 48 (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE}) 49); 50 51 52sub extension_filter 53{ 54 my $proxy = shift; 55 56 if ($proxy->flight == 1) { 57 # Change the ServerRandom so that the downgrade sentinel doesn't cause 58 # the connection to fail 59 my $message = ${$proxy->message_list}[1]; 60 $message->random("\0"x32); 61 $message->repack(); 62 return; 63 } 64 65 # We're only interested in the initial ClientHello 66 if ($proxy->flight != 0) { 67 return; 68 } 69 70 foreach my $message (@{$proxy->message_list}) { 71 if ($message->mt == TLSProxy::Message::MT_CLIENT_HELLO) { 72 # Remove all extensions and set the extension len to zero 73 $message->extension_data({}); 74 $message->extensions_len(0); 75 # Extensions have been removed so make sure we don't try to use them 76 $message->process_extensions(); 77 78 $message->repack(); 79 } 80 } 81} 82 83sub inject_duplicate_extension 84{ 85 my ($proxy, $message_type) = @_; 86 87 foreach my $message (@{$proxy->message_list}) { 88 if ($message->mt == $message_type) { 89 my %extensions = %{$message->extension_data}; 90 # Add a duplicate extension. We use cryptopro_bug since we never 91 # normally write that one, and it is allowed as unsolicited in the 92 # ServerHello 93 $message->set_extension(TLSProxy::Message::EXT_CRYPTOPRO_BUG_EXTENSION, ""); 94 $message->dupext(TLSProxy::Message::EXT_CRYPTOPRO_BUG_EXTENSION); 95 $message->repack(); 96 } 97 } 98} 99 100sub inject_duplicate_extension_clienthello 101{ 102 my $proxy = shift; 103 104 # We're only interested in the initial ClientHello 105 if ($proxy->flight == 0) { 106 inject_duplicate_extension($proxy, TLSProxy::Message::MT_CLIENT_HELLO); 107 return; 108 } 109 110 my $last_record = @{$proxy->{record_list}}[-1]; 111 $fatal_alert = 1 if $last_record->is_fatal_alert(1); 112} 113 114sub inject_duplicate_extension_serverhello 115{ 116 my $proxy = shift; 117 118 # We're only interested in the initial ServerHello 119 if ($proxy->flight == 0) { 120 return; 121 } elsif ($proxy->flight == 1) { 122 inject_duplicate_extension($proxy, TLSProxy::Message::MT_SERVER_HELLO); 123 return; 124 } 125 126 my $last_record = @{$proxy->{record_list}}[-1]; 127 $fatal_alert = 1 if $last_record->is_fatal_alert(0); 128} 129 130sub inject_unsolicited_extension 131{ 132 my $proxy = shift; 133 my $message; 134 state $sent_unsolisited_extension; 135 136 if ($proxy->flight == 0) { 137 $sent_unsolisited_extension = 0; 138 return; 139 } 140 141 # We're only interested in the initial ServerHello/EncryptedExtensions 142 if ($proxy->flight != 1) { 143 if ($sent_unsolisited_extension) { 144 my $last_record = @{$proxy->record_list}[-1]; 145 $fatal_alert = 1 if $last_record->is_fatal_alert(0); 146 } 147 return; 148 } 149 150 if ($testtype == UNSOLICITED_SERVER_NAME_TLS13) { 151 return if (!defined($message = ${$proxy->message_list}[2])); 152 die "Expecting EE message ".($message->mt)."," 153 .${$proxy->message_list}[1]->mt.", " 154 .${$proxy->message_list}[3]->mt 155 if $message->mt != TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS; 156 } else { 157 $message = ${$proxy->message_list}[1]; 158 } 159 160 my $ext = pack "C2", 161 0x00, 0x00; #Extension length 162 163 my $type; 164 if ($testtype == UNSOLICITED_SERVER_NAME 165 || $testtype == UNSOLICITED_SERVER_NAME_TLS13) { 166 $type = TLSProxy::Message::EXT_SERVER_NAME; 167 } elsif ($testtype == UNSOLICITED_SCT) { 168 $type = TLSProxy::Message::EXT_SCT; 169 } elsif ($testtype == NONCOMPLIANT_SUPPORTED_GROUPS) { 170 $type = TLSProxy::Message::EXT_SUPPORTED_GROUPS; 171 } 172 $message->set_extension($type, $ext); 173 $message->repack(); 174 $sent_unsolisited_extension = 1; 175} 176 177sub inject_cryptopro_extension 178{ 179 my $proxy = shift; 180 181 # We're only interested in the initial ClientHello 182 if ($proxy->flight != 0) { 183 return; 184 } 185 186 my $message = ${$proxy->message_list}[0]; 187 $message->set_extension(TLSProxy::Message::EXT_CRYPTOPRO_BUG_EXTENSION, ""); 188 $message->repack(); 189} 190 191# Test 1-2: Sending a duplicate extension should fail. 192$proxy->start() or plan skip_all => "Unable to start up Proxy for tests"; 193plan tests => 8; 194ok($fatal_alert, "Duplicate ClientHello extension"); 195 196SKIP: { 197 skip "TLS <= 1.2 disabled", 4 if $no_below_tls13; 198 199 $fatal_alert = 0; 200 $proxy->clear(); 201 $proxy->filter(\&inject_duplicate_extension_serverhello); 202 $proxy->clientflags("-no_tls1_3"); 203 $proxy->start(); 204 ok($fatal_alert, "Duplicate ServerHello extension"); 205 206 #Test 3: Sending a zero length extension block should pass 207 $proxy->clear(); 208 $proxy->filter(\&extension_filter); 209 $proxy->cipherc("DEFAULT:\@SECLEVEL=0"); 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