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