Home | History | Annotate | Line # | Download | only in recipes
      1      1.1  christos #! /usr/bin/env perl
      2  1.1.1.2  christos # Copyright 2018-2021 The OpenSSL Project Authors. All Rights Reserved.
      3      1.1  christos #
      4  1.1.1.2  christos # Licensed under the Apache License 2.0 (the "License").  You may not use
      5      1.1  christos # this file except in compliance with the License.  You can obtain a copy
      6      1.1  christos # in the file LICENSE in the source distribution or at
      7      1.1  christos # https://www.openssl.org/source/license.html
      8      1.1  christos 
      9      1.1  christos use strict;
     10      1.1  christos no strict 'refs';               # To be able to use strings as function refs
     11      1.1  christos use OpenSSL::Test;
     12      1.1  christos use OpenSSL::Test::Utils;
     13      1.1  christos use Errno qw(:POSIX);
     14  1.1.1.2  christos use POSIX qw(:limits_h strerror);
     15      1.1  christos 
     16  1.1.1.2  christos use Data::Dumper;
     17      1.1  christos 
     18      1.1  christos setup('test_errstr');
     19      1.1  christos 
     20      1.1  christos # In a cross compiled situation, there are chances that our
     21      1.1  christos # application is linked against different C libraries than
     22      1.1  christos # perl, and may thereby get different error messages for the
     23      1.1  christos # same error.
     24      1.1  christos # The safest is not to test under such circumstances.
     25      1.1  christos plan skip_all => 'This is unsupported for cross compiled configurations'
     26      1.1  christos     if config('CROSS_COMPILE');
     27      1.1  christos 
     28      1.1  christos # The same can be said when compiling OpenSSL with mingw configuration
     29      1.1  christos # on Windows when built with msys perl.  Similar problems are also observed
     30      1.1  christos # in MSVC builds, depending on the perl implementation used.
     31      1.1  christos plan skip_all => 'This is unsupported on MSYS/MinGW or MSWin32'
     32      1.1  christos     if $^O eq 'msys' or $^O eq 'MSWin32';
     33      1.1  christos 
     34      1.1  christos plan skip_all => 'OpenSSL is configured "no-autoerrinit" or "no-err"'
     35      1.1  christos     if disabled('autoerrinit') || disabled('err');
     36      1.1  christos 
     37  1.1.1.2  christos # OpenSSL constants found in <openssl/err.h>
     38  1.1.1.2  christos use constant ERR_SYSTEM_FLAG => INT_MAX + 1;
     39  1.1.1.2  christos use constant ERR_LIB_OFFSET => 23; # Offset of the "library" errcode section
     40  1.1.1.2  christos 
     41  1.1.1.2  christos # OpenSSL "library" numbers
     42  1.1.1.2  christos use constant ERR_LIB_NONE => 1;
     43  1.1.1.2  christos 
     44  1.1.1.2  christos # We use Errno::EXPORT_OK as a list of known errno values on the current
     45  1.1.1.2  christos # system.  libcrypto's ERR should either use the same string as perl, or if
     46  1.1.1.2  christos # it was outside the range that ERR looks at, ERR gives the reason string
     47  1.1.1.2  christos # "reason(nnn)", where nnn is the errno number.
     48      1.1  christos 
     49  1.1.1.2  christos plan tests => scalar @Errno::EXPORT_OK
     50      1.1  christos     +1                          # Checking that error 128 gives 'reason(128)'
     51      1.1  christos     +1                          # Checking that error 0 gives the library name
     52  1.1.1.2  christos     +1;                         # Check trailing whitespace is removed.
     53  1.1.1.2  christos 
     54  1.1.1.2  christos # Test::More:ok() has a sub prototype, which means we need to use the '&ok'
     55  1.1.1.2  christos # syntax to force it to accept a list as a series of arguments.
     56  1.1.1.2  christos 
     57  1.1.1.2  christos foreach my $errname (@Errno::EXPORT_OK) {
     58  1.1.1.2  christos     # The error names are perl constants, which are implemented as functions
     59  1.1.1.2  christos     # returning the numeric value of that name.
     60  1.1.1.2  christos     my $errcode = "Errno::$errname"->();
     61  1.1.1.2  christos 
     62  1.1.1.2  christos   SKIP: {
     63  1.1.1.2  christos       # On most systems, there is no E macro for errcode zero in <errno.h>,
     64  1.1.1.2  christos       # which means that it seldom comes up here.  However, reports indicate
     65  1.1.1.2  christos       # that some platforms do have an E macro for errcode zero.
     66  1.1.1.2  christos       # With perl, errcode zero is a bit special.  Perl consistently gives
     67  1.1.1.2  christos       # the empty string for that one, while the C strerror() may give back
     68  1.1.1.2  christos       # something else.  The easiest way to deal with that possible mismatch
     69  1.1.1.2  christos       # is to skip this errcode.
     70  1.1.1.2  christos       skip "perl error strings and ssystem error strings for errcode 0 differ", 1
     71  1.1.1.2  christos           if $errcode == 0;
     72  1.1.1.2  christos       # On some systems (for example Hurd), there are negative error codes.
     73  1.1.1.2  christos       # These are currently unsupported in OpenSSL error reports.
     74  1.1.1.2  christos       skip "negative error codes are not supported in OpenSSL", 1
     75  1.1.1.2  christos           if $errcode < 0;
     76  1.1.1.2  christos 
     77  1.1.1.2  christos       &ok(match_syserr_reason($errcode));
     78  1.1.1.2  christos     }
     79  1.1.1.2  christos }
     80  1.1.1.2  christos 
     81  1.1.1.2  christos # OpenSSL library 1 is the "unknown" library
     82  1.1.1.2  christos &ok(match_opensslerr_reason(ERR_LIB_NONE << ERR_LIB_OFFSET | 256,
     83  1.1.1.2  christos                             "reason(256)"));
     84  1.1.1.2  christos # Reason code 0 of any library gives the library name as reason
     85  1.1.1.2  christos &ok(match_opensslerr_reason(ERR_LIB_NONE << ERR_LIB_OFFSET |   0,
     86  1.1.1.2  christos                             "unknown library"));
     87  1.1.1.2  christos &ok(match_any("Trailing whitespace  \n\t", "?", ( "Trailing whitespace" )));
     88  1.1.1.2  christos 
     89  1.1.1.2  christos exit 0;
     90  1.1.1.2  christos 
     91  1.1.1.2  christos # For an error string "error:xxxxxxxx:lib:func:reason", this returns
     92  1.1.1.2  christos # the following array:
     93  1.1.1.2  christos #
     94  1.1.1.2  christos # ( "xxxxxxxx", "lib", "func", "reason" )
     95  1.1.1.2  christos sub split_error {
     96  1.1.1.2  christos     # Limit to 5 items, in case the reason contains a colon
     97  1.1.1.2  christos     my @erritems = split /:/, $_[0], 5;
     98      1.1  christos 
     99  1.1.1.2  christos     # Remove the first item, which is always "error"
    100  1.1.1.2  christos     shift @erritems;
    101      1.1  christos 
    102  1.1.1.2  christos     return @erritems;
    103  1.1.1.2  christos }
    104      1.1  christos 
    105  1.1.1.2  christos # Compares the first argument as string to each of the arguments 3 and on,
    106  1.1.1.2  christos # and returns an array of two elements:
    107  1.1.1.2  christos # 0:  True if the first argument matched any of the others, otherwise false
    108  1.1.1.2  christos # 1:  A string describing the test
    109  1.1.1.2  christos # The returned array can be used as the arguments to Test::More::ok()
    110  1.1.1.2  christos sub match_any {
    111  1.1.1.2  christos     my $first = shift;
    112  1.1.1.2  christos     my $desc = shift;
    113  1.1.1.2  christos     my @strings = @_;
    114  1.1.1.2  christos 
    115  1.1.1.2  christos     # ignore trailing whitespace
    116  1.1.1.2  christos     $first =~ s/\s+$//;
    117  1.1.1.2  christos 
    118  1.1.1.2  christos     if (scalar @strings > 1) {
    119  1.1.1.2  christos         $desc = "match '$first' ($desc) with one of ( '"
    120  1.1.1.2  christos             . join("', '", @strings) . "' )";
    121  1.1.1.2  christos     } else {
    122  1.1.1.2  christos         $desc = "match '$first' ($desc) with '$strings[0]'";
    123      1.1  christos     }
    124  1.1.1.2  christos 
    125  1.1.1.2  christos     return ( scalar(
    126  1.1.1.2  christos                  grep { ref $_ eq 'Regexp' ? $first =~ $_ : $first eq $_ }
    127  1.1.1.2  christos                  @strings
    128  1.1.1.2  christos              ) > 0,
    129  1.1.1.2  christos              $desc );
    130      1.1  christos }
    131      1.1  christos 
    132  1.1.1.2  christos sub match_opensslerr_reason {
    133  1.1.1.2  christos     my $errcode = shift;
    134  1.1.1.2  christos     my @strings = @_;
    135  1.1.1.2  christos 
    136  1.1.1.2  christos     my $errcode_hex = sprintf "%x", $errcode;
    137  1.1.1.2  christos     my $reason =
    138  1.1.1.2  christos         ( run(app([ qw(openssl errstr), $errcode_hex ]), capture => 1) )[0];
    139  1.1.1.2  christos     $reason =~ s|\R$||;
    140  1.1.1.2  christos     $reason = ( split_error($reason) )[3];
    141  1.1.1.2  christos 
    142  1.1.1.2  christos     return match_any($reason, $errcode_hex, @strings);
    143  1.1.1.2  christos }
    144  1.1.1.2  christos 
    145  1.1.1.2  christos sub match_syserr_reason {
    146  1.1.1.2  christos     my $errcode = shift;
    147  1.1.1.2  christos 
    148  1.1.1.2  christos     my @strings = ();
    149  1.1.1.2  christos     # The POSIX reason string
    150  1.1.1.2  christos     push @strings, eval {
    151  1.1.1.2  christos           # Set $! to the error number...
    152  1.1.1.2  christos           local $! = $errcode;
    153  1.1.1.2  christos           # ... and $! will give you the error string back
    154  1.1.1.2  christos           $!
    155  1.1.1.2  christos     };
    156  1.1.1.2  christos     # Occasionally, we get an error code that is simply not translatable
    157  1.1.1.2  christos     # to POSIX semantics on VMS, and we get an error string saying so.
    158  1.1.1.2  christos     push @strings, qr/^non-translatable vms error code:/ if $^O eq 'VMS';
    159  1.1.1.2  christos     # The OpenSSL fallback string
    160  1.1.1.2  christos     push @strings, "reason($errcode)";
    161  1.1.1.2  christos 
    162  1.1.1.2  christos     return match_opensslerr_reason(ERR_SYSTEM_FLAG | $errcode, @strings);
    163  1.1.1.2  christos }
    164