1#! /usr/bin/env perl
2# Copyright 2023-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 warnings;
11
12use IPC::Open3;
13use OpenSSL::Test qw/:DEFAULT srctop_file bldtop_file/;
14use OpenSSL::Test::Utils;
15use Symbol 'gensym';
16
17my $test_name = "test_ocsp_cert_chain";
18setup($test_name);
19
20plan skip_all => "$test_name requires OCSP support"
21    if disabled("ocsp");
22plan skip_all => "$test_name requires EC cryptography"
23    if disabled("ec");
24plan skip_all => "$test_name requires sock enabled"
25    if disabled("sock");
26plan skip_all => "$test_name requires TLS enabled"
27    if alldisabled(available_protocols("tls"));
28plan skip_all => "$test_name is not available Windows or VMS"
29    if $^O =~ /^(VMS|MSWin32|msys)$/;
30
31plan tests => 3;
32
33my $shlib_wrap   = bldtop_file("util", "shlib_wrap.sh");
34my $apps_openssl = bldtop_file("apps", "openssl");
35
36my $index_txt             = srctop_file("test", "ocsp-tests", "index.txt");
37my $ocsp_pem              = srctop_file("test", "ocsp-tests", "ocsp.pem");
38my $intermediate_cert_pem = srctop_file("test", "ocsp-tests", "intermediate-cert.pem");
39
40my $server_pem            = srctop_file("test", "ocsp-tests", "server.pem");
41
42sub run_test {
43
44    # this test starts two servers that listen on respective ports.
45    # that can be problematic since the ports may not be available
46    # (e.g. when multiple instances of the test are run on the same
47    # machine).
48
49    # to avoid this, we specify port 0 when staring each server, which
50    # causes the OS to provide a random unused port.
51
52    # using a random port with s_server is straightforward.  doing so
53    # with the ocsp responder required some investigation because the
54    # url for the ocsp responder is usually included in the server's
55    # cert (normally, in the authority-information-access extension,
56    # and it would be complicated to change that when the test
57    # executes).  however, s_server has an option "-status_url" that
58    # can be used to specify a fallback url when no url is specified
59    # in the cert.  that is what we do here.
60
61    # openssl ocsp -port 0 -index index.txt -rsigner ocsp.pem -CA intermediate-cert.pem
62    my @ocsp_cmd = ("ocsp", "-port", "0", "-index", $index_txt, "-rsigner", $ocsp_pem, "-CA", $intermediate_cert_pem);
63    my $ocsp_pid = open3(my $ocsp_i, my $ocsp_o, my $ocsp_e = gensym, $shlib_wrap, $apps_openssl, @ocsp_cmd);
64
65    ## ipv4
66    # ACCEPT 0.0.0.0:19254 PID=620007
67    ## ipv6
68    # ACCEPT [::]:19254 PID=620007
69    my $port = "0";
70    while (<$ocsp_o>) {
71        print($_);
72        chomp;
73        if (/^ACCEPT 0.0.0.0:(\d+)/) {
74            $port = $1;
75            last;
76        } elsif (/^ACCEPT \[::\]:(\d+)/) {
77            $port = $1;
78            last;
79        } else {
80            last;
81        }
82    }
83    ok($port ne "0", "ocsp server port check");
84    my $ocsp_port = $port;
85
86    print("ocsp server ready, listening on port $ocsp_port\n");
87
88    # openssl s_server -accept 0 -naccept 1 \
89    #                  -cert server.pem -cert_chain intermediate-cert.pem \
90    #                  -status_verbose -status_url http://localhost:19254/ocsp
91    my @s_server_cmd = ("s_server", "-accept", "0", "-naccept", "1",
92                        "-cert", $server_pem, "-cert_chain", $intermediate_cert_pem,
93                        "-status_verbose", "-status_url", "http://localhost:${ocsp_port}/ocsp");
94    my $s_server_pid = open3(my $s_server_i, my $s_server_o, my $s_server_e = gensym, $shlib_wrap, $apps_openssl, @s_server_cmd);
95
96    # ACCEPT 0.0.0.0:45921
97    # ACCEPT [::]:45921
98    $port = "0";
99    while (<$s_server_o>) {
100        print($_);
101        chomp;
102        if (/^ACCEPT 0.0.0.0:(\d+)/) {
103            $port = $1;
104            last;
105        } elsif (/^ACCEPT \[::\]:(\d+)/) {
106            $port = $1;
107            last;
108        } elsif (/^Using default/) {
109            ;
110        } else {
111            last;
112        }
113    }
114    ok($port ne "0", "s_server port check");
115    my $server_port = $port;
116
117    print("s_server ready, listening on port $server_port\n");
118
119    # openssl s_client -connect localhost:45921 -status -verify_return_error
120    my @s_client_cmd = ("s_client", "-connect", "localhost:$server_port", "-status", "-verify_return_error");
121    my $s_client_pid = open3(my $s_client_i, my $s_client_o, my $s_client_e = gensym, $shlib_wrap, $apps_openssl, @s_client_cmd);
122
123    waitpid($s_client_pid, 0);
124    kill 'HUP', $s_server_pid, $ocsp_pid;
125
126    ### the output from s_server that we want to check is written to its stderr
127    ###    cert_status: ocsp response sent:
128
129    my $resp = 0;
130    while (<$s_server_e>) {
131        print($_);
132        chomp;
133        if (/^cert_status: ocsp response sent:/) {
134            $resp = 1;
135            last;
136        }
137    }
138    ok($resp == 1, "check s_server sent ocsp response");
139}
140
141run_test();
142