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