Server : Apache System : Linux copper.netcy.com 2.6.32-754.27.1.el6.centos.plus.x86_64 #1 SMP Thu Jan 30 13:54:25 UTC 2020 x86_64 User : montcaro ( 581) PHP Version : 7.4.28 Disable Function : NONE Directory : /scripts/ |
#!/usr/bin/perl # cpanel - scripts/fix-cpanel-perl Copyright 2019 cPanel, L.L.C. # All rights Reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited # This script was written to provide a means to recover from a catostropic loss # of cpanel-perl RPMs so that you cannot run check_cpanel_rpms and/or upcp to get # all your lost cPanel files back. It is automatically invoked if need-be if # check_cpanel_rpms cannot run. package scripts::fixcpanelperl; use strict; use warnings; use IPC::Open3 (); use POSIX (); my $COLOR_RED = 31; my $COLOR_YELLOW = 33; our $CPANEL_CONFIG_FILE = q[/var/cpanel/cpanel.config]; our $SIG_VALIDATION_CPCONF_KEY = 'signature_validation'; sub colorize_bold { my ( $color, $msg ) = @_; return $msg if !defined $color || -e q{/var/cpanel/disable_cpanel_terminal_colors}; $msg ||= ''; return chr(27) . '[1;' . $color . 'm' . $msg . chr(27) . '[0;m'; } sub DEBUG($) { return _MSG( 'DEBUG', " " . shift ) } ## no critic(ProhibitSubroutinePrototypes) sub ERROR($) { return _MSG( 'ERROR', colorize_bold( $COLOR_RED, shift ) ) } ## no critic(ProhibitSubroutinePrototypes) sub WARN($) { return _MSG( 'WARN', colorize_bold( $COLOR_YELLOW, shift ) ) } ## no critic(ProhibitSubroutinePrototypes) sub INFO($) { return _MSG( 'INFO', shift ) } ## no critic(ProhibitSubroutinePrototypes) sub FATAL($) { _MSG( 'FATAL', colorize_bold( $COLOR_RED, shift ) ); die "\n"; } ## no critic(ProhibitSubroutinePrototypes) # Cached and used all over the place. my ( $wget_bin, $wget_args, $gpg_bin ); my ( $distro, $distro_version, $distro_arch ); my %sha; exit script() unless caller(); sub script { return 0 if cpanel_perl_is_stable(); ERROR("Core cpanel-perl modules have been found to be corrupt. Attempting to correct this.") unless $ENV{CPANEL_BASE_INSTALL}; ( $wget_bin, $wget_args ) = get_download_tool_binary(); $gpg_bin = gpg_bin(); ( $distro, $distro_version, $distro_arch ) = check_system_support(); fetch_and_install_gpg_keys() unless $ENV{CPANEL_BASE_INSTALL_GPG_KEYS_IMPORTED}; my ( $rpm_version_source, @rpms ) = rpms_to_download(); my $rpm_downloads_dir = '/usr/local/cpanel/tmp/rpm_downloads'; my $rpm_url_base = "/RPM/$rpm_version_source/centos/$distro_version/x86_64"; my $core_perl_rpm = shift @rpms; get_rpm_sha512( $rpm_downloads_dir, $rpm_url_base, $rpm_version_source ); chdir $rpm_downloads_dir; my $core_perl_pid; if ( $core_perl_pid = fork() ) { } else { wget_and_validate_file( $core_perl_rpm, $rpm_url_base, $rpm_downloads_dir ); system( qw{/bin/rpm -Uvh --force}, $core_perl_rpm ); exit(0); } foreach my $rpm_file (@rpms) { wget_and_validate_file( $rpm_file, $rpm_url_base, $rpm_downloads_dir ); } { waitpid( $core_perl_pid, 0 ); FATAL "Core perl RPM transaction failed" unless $? == 0; } system( qw{/bin/rpm -Uvh --force}, @rpms ); FATAL "RPM transaction failed" unless $? == 0; # updatenow.static will do the needful during an install. return 0 if $ENV{CPANEL_BASE_INSTALL}; if ( !-x '/usr/local/cpanel/scripts/check_cpanel_rpms' ) { FATAL "Unable to run scripts/check_cpanel_rpms. You will need to run updatenow.static and then re-run /usr/local/cpanel/scripts/check_cpanel_rpms --fix"; } exec(qw{/usr/local/cpanel/scripts/check_cpanel_rpms --fix --long-list --no-digest}) or FATAL "Failed to exec /usr/local/cpanel/scripts/check_cpanel_rpms --fix"; return 255; } sub rpms_to_download { return "11.86", # The main perl rpm must always come first qw { cpanel-perl-530-5.30.0-4.cp1186.x86_64.rpm cpanel-perl-530-common-sense-3.74-1.cp1186.noarch.rpm cpanel-perl-530-CDB_File-0.99-1.cp1186.x86_64.rpm cpanel-perl-530-IO-SigGuard-0.14-1.cp1186.noarch.rpm cpanel-perl-530-JSON-XS-3.04-1.cp1186.x86_64.rpm cpanel-perl-530-Compress-Raw-Lzma-2.087-1.cp1186.x86_64.rpm cpanel-perl-530-Proc-FastSpawn-1.2-1.cp1186.x86_64.rpm cpanel-perl-530-Try-Tiny-0.30-1.cp1186.noarch.rpm cpanel-perl-530-Types-Serialiser-1.0-1.cp1186.noarch.rpm cpanel-perl-530-YAML-Syck-1.31-1.cp1186.x86_64.rpm cpanel-perl-530-Net-SSLeay-1.88-1.cp1186.x86_64.rpm cpanel-perl-530-IO-Socket-SSL-2.066-2.cp1186.noarch.rpm }; } sub cpanel_perl_modules { # Throw out 11.70 and the first RPM (perl) my ( undef, undef, @rpms ) = rpms_to_download(); my @modules; foreach my $rpm (@rpms) { $rpm =~ s/^cpanel-perl-530-//; $rpm =~ s/-\d.+$//; $rpm =~ s/-/::/g; push @modules, $rpm; } return @modules; } sub cpanel_perl_is_stable { my $command = '/usr/local/cpanel/3rdparty/bin/perl'; $command .= " -M$_" foreach cpanel_perl_modules(); my $got = `$command -E'print q{ok}' 2>&1`; return ( !$? && $got && $got eq 'ok' ) ? 1 : 0; } sub get_rpm_sha512 { my ( $rpm_downloads_dir, $rpm_url_base, $rpm_version_source ) = @_; # Setup the directory as best we can. unlink( '/usr/local/cpanel/tmp', $rpm_downloads_dir ); mkdir '/usr/local/cpanel/tmp'; mkdir $rpm_downloads_dir; -d $rpm_downloads_dir or FATAL("Can't make directory $rpm_downloads_dir "); my $sha_file = "$rpm_downloads_dir/rpm.$rpm_version_source.sha512"; my $sig_file = "$rpm_downloads_dir/rpm.$rpm_version_source.sha512.asc"; # wget_file( "$rpm_url_base/rpm.sha512.asc", $sig_file ); wget_file( "$rpm_url_base/rpm.sha512", $sha_file ); verify_file_signature( $sha_file, $sig_file, "$rpm_url_base/rpm.sha512" ); open( my $fh, '<', $sha_file ) or FATAL("Can't read $sha_file"); while ( my $line = <$fh> ) { chomp $line; my ( $sha, $file ) = split( qr{\s+}, $line ); $sha{$file} = $sha; } close $fh; return; } sub get_download_tool_binary { for my $bin (qw(/bin/wget /usr/bin/wget /usr/local/bin/wget)) { next if ( !-e $bin ); next if ( !-x _ ); next if ( -z _ ); return ( $bin, ' -nv --no-dns-cache --tries=20 --timeout=60 --dns-timeout=60 --read-timeout=30 --waitretry=1 --retry-connrefused -O' ) if ( `$bin --version` =~ m/GNU\s+Wget\s+\d+\.\d+/ims ); } FATAL "Can't bootstrap cpanel-perl without wget. Try: yum -y install wget"; return; } sub gpg_bin { for my $bin (qw(/bin/gpg /usr/bin/gpg /usr/local/bin/gpg)) { next if ( !-e $bin ); next if ( !-x _ ); next if ( -z _ ); return $bin; } FATAL "Can't bootstrap cpanel-perl without gpg. Try: yum -y install gnupg2"; return; } sub _MSG { my $level = shift; my $msg = shift || ''; chomp $msg; my $message_caller_depth = 1; my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime; my ( $package, $filename, $line ) = caller($message_caller_depth); my $stamp_msg = sprintf( "%04d-%02d-%02d %02d:%02d:%02d %4s (%5s): %s\n", $year + 1900, $mon + 1, $mday, $hour, $min, $sec, $line, $level, $msg ); print $stamp_msg; return; } sub get_update_source { my $update_source = 'httpupdate.cpanel.net'; my $source_file = '/etc/cpsources.conf'; if ( -r $source_file && -s $source_file ) { # pull in from cpsources.conf if it's set. open( my $fh, "<", $source_file ) or return $update_source; while (<$fh>) { next if ( $_ !~ m/^\s*HTTPUPDATE\s*=\s*(\S+)/ ); $update_source = "$1"; FATAL("HTTPUPDATE is set to '$update_source' in the $source_file file.") if ( !$update_source ); last; } } return $update_source; } sub wget_and_validate_file { my ( $rpm_file, $rpm_url_base, $rpm_downloads_dir ) = @_; my $rpm_file_path = wget_file( "$rpm_url_base/$rpm_file", "$rpm_downloads_dir/$rpm_file" ); my $result = `/usr/bin/sha512sum $rpm_file_path 2>&1`; my ($sha) = $result =~ m/^([a-fA-F0-9]+)/; if ( $sha{$rpm_file} ne $sha ) { FATAL("Couldn't verify the expected sha ($sha{$rpm_file}) for $rpm_file. Got $sha"); } return; } sub wget_file { my ( $url, $dest_file ) = @_; $url = 'http://' . get_update_source() . $url; DEBUG "Retrieving $url"; my $output = `$wget_bin $wget_args '$dest_file' $url 2>&1`; if ( !-e $dest_file || -z $dest_file ) { unlink $dest_file; FATAL "The system could not fetch the $dest_file file: $output"; } return $dest_file; } sub signature_validation_enabled { my $config = read_config(); return 1 unless defined $config->{$SIG_VALIDATION_CPCONF_KEY}; return 0 if $config->{$SIG_VALIDATION_CPCONF_KEY} eq '0' || lc( $config->{$SIG_VALIDATION_CPCONF_KEY} ) eq 'off'; return 1; } sub verify_file_signature { my ( $file, $sig, $url ) = @_; if ( !signature_validation_enabled() ) { INFO "Skipping signature validation [currently disabled in cpanel.config]"; return; } INFO "FILE - $file"; INFO "SIG - $sig"; INFO "URL - $url"; my @gpg_args = ( '--logger-fd', '1', '--status-fd', '1', '--homedir', gpg_homedir(), '--verify', $sig, $file, ); # Verify the validity of the GPG signature. # Information on these return values can be found in 'doc/DETAILS' in the GnuPG source. my ( %notes, $curnote ); my ( $gpg_out, $success, $status ); my $gpg_pid = IPC::Open3::open3( undef, $gpg_out, undef, $gpg_bin, @gpg_args ); while ( my $line = readline($gpg_out) ) { if ( $line =~ /^\[GNUPG:\] VALIDSIG ([A-F0-9]+) (\d+-\d+-\d+) (\d+) ([A-F0-9]+) ([A-F0-9]+) ([A-F0-9]+) ([A-F0-9]+) ([A-F0-9]+) ([A-F0-9]+) ([A-F0-9]+)$/ ) { $status = "Valid signature for $file"; $success = 1; } elsif ( $line =~ /^\[GNUPG:\] NOTATION_NAME (.+)$/ ) { $curnote = $1; $notes{$curnote} = ''; } elsif ( $line =~ /^\[GNUPG:\] NOTATION_DATA (.+)$/ ) { $notes{$curnote} .= $1; } elsif ( $line =~ /^\[GNUPG:\] BADSIG ([A-F0-9]+) (.+)$/ ) { $status = "Invalid signature for $file."; } elsif ( $line =~ /^\[GNUPG:\] NO_PUBKEY ([A-F0-9]+)$/ ) { $status = "Could not find public key in keychain."; } elsif ( $line =~ /^\[GNUPG:\] NODATA ([A-F0-9]+)$/ ) { $status = "Could not find a GnuPG signature in the signature file."; } } waitpid( $gpg_pid, 0 ); $status ||= "Unknown error from gpg."; $status .= " ($file)"; if ($success) { INFO $status; } else { FATAL $status; } # At this point, the signature should be valid. # We now need to check to see if the filename signature notation is correct. $url =~ s/\.bz2$//; if ( defined( $notes{'filename@gpg.notations.cpanel.net'} ) ) { my $file_note = $notes{'filename@gpg.notations.cpanel.net'}; if ( $file_note ne $url ) { FATAL "Filename notation ($file_note) does not match URL ($url)."; } } else { FATAL "Signature does not contain a filename notation."; } return; } sub fetch_and_install_gpg_keys { my $pub_keys = public_keys(); _create_gpg_homedir(); foreach my $key ( @{ keys_to_download() } ) { INFO("Downloading GPG public key, $pub_keys->{$key}"); my $target = secure_downloads() . $pub_keys->{$key}; my $dest = gpg_homedir() . "/" . $pub_keys->{$key}; my $wget_cmd = $wget_args . " " . $dest . " " . $target; my $wget_out = `$wget_bin $wget_cmd 2>&1`; if ( !-e $dest ) { WARN("Could not download GPG public key at $target : $wget_out"); return; } my $gpg_cmd = $gpg_bin . " -q --homedir " . gpg_homedir() . " --import " . $dest; `$gpg_cmd`; } return; } sub _create_gpg_homedir { mkdir( gpg_homedir(), 0700 ) if !-e gpg_homedir(); return; } sub invalid_system { my $message = shift || ''; chomp $message; ERROR "$message"; ERROR "The system detected an unsupported distribution. cPanel & WHM only supports CentOS 6 and 7, Red Hat Enterprise Linux® 6 and 7, and CloudLinux™ 6 and 7."; FATAL "Please reinstall cPanel & WHM from a valid distribution."; return; # Fatal will die. } sub get_distro_release_rpm { # /etc/redhat-release or /etc/system-release must be present my ( $rhel_release, $amazon_release ) = ( '/etc/redhat-release', '/etc/system-release' ); my $distro_release; if ( -e $rhel_release ) { $distro_release = $rhel_release; } elsif ( -e $amazon_release ) { $distro_release = $amazon_release; } else { invalid_system("The system could not detect a valid release file for this distribution"); } chomp( my $release_rpm = `rpm -qf $distro_release` ); return $release_rpm; } sub _distro_name { my ( $distro, $full ) = @_; for my $names ( [ 'centos', 'CentOS', 'CentOS' ], [ 'redhat', 'Red Hat', 'Red Hat Enterprise Linux®' ], [ 'cloud', 'CloudLinux', 'CloudLinux™' ], [ 'amazon', 'Amazon Linux', 'Amazon Linux' ], ) { return $names->[ $full ? 2 : 1 ] if $distro eq $names->[0]; } return $distro; } sub check_system_support { # Some of these variables are unused *as of now*. However! some of these values may be useful for 'filling in the blanks' when the RPM check we do fails to provide all required info. # For now, only the $system and $machine variables are used, $machine only when Amazon Linux is detected. See https://metacpan.org/pod/POSIX#uname for more info. my ( $system, $nodename, $release, $version, $machine ) = POSIX::uname(); if ( $system !~ m/linux/i ) { invalid_system("Could not detect version for operating system"); } my $release_rpm = get_distro_release_rpm(); $release_rpm or invalid_system("RPMs do not manage release file."); # an rpm we recognize must manage it. # We require a non capturing group while still supporting CloudLinux 5, otherwise this will fail to install as it does not include the arch. my ( $distro_type, $distro_version, $distro_arch ) = $release_rpm =~ m{^(\D+)-([\d\.]+).*?(?:\.([a-z0-9_]+))?$}ms; $distro_version = $distro_version + 0; $distro_version or invalid_system("The system found that the unexpected '$release_rpm' RPM manages the release file."); # This is required for CloudLinux 5 as they do not set an arch for their rpm. So we want to ignore it. $distro_arch ||= ''; # That RPM must have redhat or centos in the name. $distro_type =~ m/centos|redhat|enterprise-release|system-release|cloud/i or invalid_system("The system found that the unexpected '$distro_type' RPM manages the release file."); $distro_type =~ s/-release//imsg; my $distro; if ( $distro_type eq 'enterprise' ) { $distro = 'redhat'; } elsif ( $distro_type eq 'system' ) { $distro = 'amazon'; $distro_arch = $machine if $distro_arch eq 'noarch'; # SEE CPANEL-8050 } else { $distro = $distro_type; } INFO _distro_name($distro) . " $distro_version (Linux) detected!"; # Handle redhat/centos versioning if ( $distro ne 'amazon' ) { $distro_version =~ s/\.\d+//; # Strip off the decimal on Cloud Linux 7 # The version number must be 6 or 7. ( int($distro_version) <= 7 && $distro_version >= 6 ) or invalid_system( "cPanel, L.L.C. does not support " . _distro_name($distro) . " version $distro_version." ); # Supported distros for installer: redhat/red hat enterprise/cloud/centos/amazon $distro = ( $distro =~ m/redhat|hat enterprise/i ) ? 'redhat' : ( $distro =~ m/cloud/i ) ? 'cloud' : 'centos'; } else { $distro_version = '6'; # Amazon Linux needs to act like CentOS 6 for RPMs. } return ( $distro, $distro_version, $distro_arch ); } our $CACHE_CONFIG; sub read_config { my $file = $CPANEL_CONFIG_FILE; return $CACHE_CONFIG if $CACHE_CONFIG; my $config = {}; open( my $fh, "<", $file ) or return $config; while ( my $line = readline $fh ) { chomp $line; if ( $line =~ m/^\s*([^=]+?)\s*$/ ) { my $key = $1 or next; # Skip loading the key if it's undef or 0 $config->{$key} = undef; } elsif ( $line =~ m/^\s*([^=]+?)\s*=\s*(.*?)\s*$/ ) { my $key = $1 or next; # Skip loading the key if it's undef or 0 $config->{$key} = $2; } } $CACHE_CONFIG = $config; return $config; } sub keys_to_download { my $config = read_config(); my $keyrings = gpg_keyrings(); if ( !defined $config->{'signature_validation'} ) { my $mirror = get_update_source(); if ( $mirror =~ /^(?:.*\.dev|qa-build|next)\.cpanel\.net$/ ) { return $keyrings->{'development'}; } else { return $keyrings->{'release'}; } } elsif ( $config->{'signature_validation'} =~ /^Release and (?:Development|Test) Keyrings$/ ) { return $keyrings->{'development'}; } else { return $keyrings->{'release'}; } } # The installer may set $ENV{'CPANEL_BASE_INSTALL_GPG_KEYS_IMPORTED'} # to true in which case the keys will be in /var/cpanel/.gpgtmpdir sub gpg_homedir { return '/var/cpanel/.gpgtmpdir'; } sub public_keys { return { 'release' => 'cPanelPublicKey.asc', 'development' => 'cPanelDevelopmentKey.asc', }; } sub secure_downloads { return 'https://securedownloads.cpanel.net/'; } sub gpg_keyrings { return { 'release' => ['release'], 'development' => [ 'release', 'development' ], }; } 1;