#!/bin/sh
eval '/usr/local/cpanel/scripts/fix-cpanel-perl && exec /usr/local/cpanel/3rdparty/bin/perl -x -- $0 ${1+"$@"}' ## no critic qw(ProhibitStringyEval RequireUseStrict RequireUseWarnings)
if 0;
#!/usr/bin/perl
# cpanel - scripts/check_cpanel_rpms Copyright 2015 cPanel, Inc.
# All rights Reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
package scripts::check_cpanel_rpms;
# This script will handle repairing/listing broken RPMs
# -
# Q: What is a broken aka altered RPM?
# A: Any files output by a rpm -V RPM_PACKAGE_NAME that are listed to have a different MD5 sum or permission change indicate a broken RPM.
use strict;
use warnings;
use Cpanel::Usage ();
use Cpanel::Update::Logger ();
use Cpanel::RPM::Versions::File ();
use Cpanel::TempFile ();
use Cpanel::LoadModule ();
exit __PACKAGE__->script(@ARGV) unless caller();
sub script {
my ( $class, @argv ) = @_;
local $| = 1;
my $list_only = 0;
my $long_list = 0;
my $fix = 0;
my $contact = 0;
my $targets = 0;
my $nodir = 0;
my $skip_digest_check = 0;
my $skip_broken_check = 0;
my $download_only = 0;
my %opts = (
'long-list' => \$long_list,
'list-only' => \$list_only,
'fix' => \$fix,
'notify' => \$contact,
'targets' => \$targets,
'nodir' => \$nodir,
'no-digest' => \$skip_digest_check,
'no-broken' => \$skip_broken_check,
'download-only' => \$download_only,
);
my $self = bless {
'notification_rpms' => [],
}, $class;
my $status = Cpanel::Usage::wrap_options( { strict => 1 }, \@argv, \&usage, \%opts ) || 0;
usage(1) if $status > 2;
usage(1) if ( $list_only && $fix );
my $logger = Cpanel::Update::Logger->new( { 'stdout' => 1, 'log_level' => 'info' } );
my $temp;
my %directory_options = ();
if ($nodir) {
$temp = Cpanel::TempFile->new;
my $tempdir = $temp->dir;
$directory_options{'directory'} = $tempdir;
}
# Make sure all rpm calls provide expected
# output format
local $ENV{'LC_ALL'} = 'C';
local $ENV{'LANG'} = 'C';
my $v;
if ($targets) {
my @targets = split( /\s*,\s*/, $targets );
$v = Cpanel::RPM::Versions::File->new( { 'only_targets' => \@targets, logger => $logger, %directory_options } );
}
else {
$v = Cpanel::RPM::Versions::File->new( { logger => $logger, %directory_options } );
}
# need to be sure that the update config is saved if we detect an *disable file
$v->dir_files()->save() if $v->dir_files()->config_changed();
my $found = 0;
my $installed_rpms = $v->list_rpms_in_state("installed");
my $upgraded_rpms = $v->list_rpms_in_state("upgraded");
my $missing_rpms = $v->install_hash( $installed_rpms, $upgraded_rpms );
if (%$missing_rpms) {
$found++;
log_header($logger);
$logger->info("The following RPMs are missing from your system:");
foreach my $rpm ( sort keys %$missing_rpms ) {
$logger->info("$rpm-$missing_rpms->{$rpm}");
$self->_store_rpm_for_notify( "$rpm-$missing_rpms->{$rpm}", 'missing' );
}
}
my $unnecessary_rpms = $v->uninstall_hash($installed_rpms);
if (%$unnecessary_rpms) {
$found++;
log_header($logger);
$logger->info(" ");
$logger->info("The following RPMs are unneeded on your system and should be uninstalled:");
foreach my $rpm ( sort keys %$unnecessary_rpms ) {
$logger->info("$rpm-$unnecessary_rpms->{$rpm}");
$self->_store_rpm_for_notify( "$rpm-$unnecessary_rpms->{$rpm}", 'unnecessary' );
}
}
# The hash reference returned by get_dirty_rpms() looks like:
# {
# 'nsd,3.2.9-2.cp1136' => [
# '/etc/nsd'
# ]
# };
# Delete cached results otherwise this will need to be run twice if a RPM is altered.
unless ($skip_broken_check) {
delete $v->{'rpm_qa'};
}
# Suppress warnings from get_dirty_rpms call.
$v->logger->set_logging_level('ERROR');
my $broken_rpms = $skip_broken_check ? {} : $v->get_dirty_rpms($skip_digest_check);
$v->logger->set_logging_level('INFO');
my @broken_rpms_list = sort keys %$broken_rpms;
if (%$broken_rpms) {
$found++;
log_header($logger);
$logger->info(" ");
$logger->info("The following files were found to be altered from their original RPM:") if ( !$long_list );
foreach my $rpm ( sort keys %$broken_rpms ) {
foreach ( @{ $broken_rpms->{$rpm} } ) {
my ( $broken_file, $reason ) = @$_;
$logger->info("$rpm,$reason,$broken_file") if $long_list;
$self->_store_rpm_for_notify( "$rpm-$broken_file", 'broken', $reason );
}
$logger->info("$rpm") if !$long_list;
}
$logger->info(" ");
}
if ($found) {
# Fix if necessary.
if ($download_only) {
$v->download_all();
}
elsif ( !$list_only ) {
if ( !$fix ) {
$fix = prompt('Do you want to repair these RPMs?');
}
if ($fix) {
$v->logger->{'stdout'} = 1;
$v->logger->set_logging_level('INFO');
$v->reinstall_rpms(@broken_rpms_list);
foreach my $rpm ( @{ $self->{'notification_rpms'} } ) {
# TODO: reinstall_rpms assumes success here
$rpm->{'status'} = $rpm->{'status'} eq 'broken' ? 'repaired' : $rpm->{'status'};
}
}
}
$self->notify() if $contact;
}
return $logger->get_need_notify ? 2 : 0;
}
sub _store_rpm_for_notify {
my ( $self, $rpm, $status, $info ) = @_;
push @{ $self->{'notification_rpms'} }, {
'rpm' => $rpm,
'status' => $status, #unnecessary #broken #missing #repaired
'info' => $info,
};
return 1;
}
sub notify {
my ($self) = @_;
Cpanel::LoadModule::load_perl_module("Cpanel::iContact::Class::Check::CpanelRPMs") || return;
require Cpanel::Notify;
Cpanel::Notify::notification_class(
'class' => 'Check::CpanelRPMs',
'application' => 'Check::CpanelRPMs',
'constructor_args' => [
origin => 'rpmcheck',
rpms => $self->{'notification_rpms'},
]
);
}
sub prompt {
return 0 if ( !-t STDIN );
print shift @_;
my $response = '';
eval { require Term::ReadKey };
unless ($@) {
local $SIG{INT} = sub { Term::ReadKey::ReadMode( 'restore', *STDIN ); exit };
Term::ReadKey::ReadMode( 'noecho', *STDIN );
Term::ReadKey::ReadMode( 'raw', *STDIN );
$response = '';
while ( $response !~ m{^[ynYN]$} ) {
print "(y/n):\n";
$response = getc(*STDIN);
}
Term::ReadKey::ReadMode( 'restore', *STDIN );
print "$response\n";
}
else {
my $response = <>;
while ( $response !~ m{^[ynYN]$} ) {
print "(y/n):\n";
$response = getc(*STDIN);
}
}
return ( $response =~ m{[yY]} ) ? 1 : 0;
}
{
my $_has_header;
sub log_header {
return if $_has_header;
my ($logger) = @_;
my $header .= <<END;
Problems were detected with cPanel-provided files which are RPM controlled.
If you did not make these changes intentionally, you can correct them by running:
> /usr/local/cpanel/scripts/check_cpanel_rpms --fix
END
$logger->info('');
foreach my $line ( split( /\n/, $header ) ) {
$logger->info($line);
}
$_has_header = 1;
}
}
sub usage {
my $exit_code = shift || 0;
my $program_name = $0;
$program_name =~ s{/usr/local/cpanel/}{};
print qq{
$program_name
Responsible for validating the integrity of cPanel-managed RPMs and their corresponding files.
This script detects if there are any RPMs that have been unexpectedly altered. Files are
considered altered:
- If their ownership has changed,
- If they contain an MD5 mismatch
- If they are a symlink, the file points to the wrong path.
Any RPMs that should be installed or uninstalled will also be detected.
$program_name offers the opportunity to repair these issues by reinstalling the RPM that contains the
altered file(s).
Usage:
$program_name [options]
Options:
--fix - Show any problems and automatically correct them.
--list-only - Only list altered RPMs and then exit.
--download-only - Downloads rpms and exit
--long-list - Show in a more easily parsed format the altered RPMs and files.
--notify - Send out a notification regarding any altered RPMs. Additionally,
it will describe any action that was taken.
--targets - Filter RPMs based on provided targets (comma delimited).
--nodir - This option will prevent the directory /var/cpanel/rpm.versions.d from being read.
--no-digest - This option will speed up “$0” run by applying
rpm options “--nodigest” and “--nomd5” when rpm -V check is performed.
--no-broken - This option will prevent the system from checking for broken rpms
and it will only install missing and uninstall unneeded ones.
Checks Performed:
$program_name runs the rpm -V check on all cPanel-managed RPMs. rpm -V determines if the files have
changed since their installation; configuration and documentation files are ignored in this process. The
table below shows the changes detected in the output of rpm -V.
Note: If the output indicates that only Mode or mTime have changed, then that file will not be
labeled as "changed."
Check | Description
S | File Size differs.
M | Mode differs (includes permissions and file type).
5 | MD5 sum differs.
D | Device major/minor number mismatch.
L | readLink(2) path mismatch.
U | User ownership differs.
G | Group ownership differs.
T | mTime differs.
};
exit $exit_code;
}
1;
Copyright 2K16 - 2K18 Indonesian Hacker Rulez