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