#!/usr/local/cpanel/3rdparty/bin/perl # cpanel - scripts/check_users_my_cnf Copyright 2022 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 use strict; use warnings; use Cpanel::PwCache::PwEnt (); use Cpanel::PwCache (); use Cpanel::AccessIds (); use Getopt::Param (); use Cpanel::DBI::Mysql (); use Cpanel::MysqlUtils::Running (); my $prm = Getopt::Param->new( { 'help_coderef' => sub { print <<"END_USAGE"; Check users for ~/.my.cnf files that do not work and disable them. By default it only has output when a bad ~/.my.cnf is detected. $0 --help - this screen $0 --verbose - Display verbose information about the user's being checked and ~/.my.cnf status. $0 --dryrun - do not disable an invalid ~/.my.cnf just report the problem $0 --user=USERA [--user=USERB} - specify a user (or users by using more than one --user flag) to check instead of checking all users $0 --perm-only - Do not test the connectivity, only do the mode and ownership check. END_USAGE exit; }, } ); my %users; @users{ $prm->param('user') } = (); my $hasuser = $prm->param('user') ? 1 : 0; my $verbose = $prm->param('verbose') ? 1 : 0; my $dryrun = $prm->param('dryrun') ? 1 : 0; my $justprm = $prm->param('perm-only') ? 1 : 0; _mysql_is_up_or_stop() if !$justprm; my $root_home = ( Cpanel::PwCache::getpwnam('root') )[7]; my @PW; Cpanel::PwCache::PwEnt::setpwent(); sub _iterate_pw { ## no critic qw(ProhibitExcessComplexity) while ( @PW = Cpanel::PwCache::PwEnt::getpwent() ) { next if $hasuser && !exists $users{ $PW[0] }; print "Starting '$PW[0]' ...\n" if $verbose; my $file = "$PW[7]/.my.cnf"; if ( -e $file ) { # if ( chdir $PW[7] ) { my $check = sub { # untaint my ($_file) = $file =~ m{(.*)}; # check perms before connectivity test since bad perms can prevent connection # Warning: World-writable config file '/root/.my.cnf' is ignored my ( $mode, $uid, $gid ) = ( stat($_file) )[ 2, 4, 5 ]; my $perm = sprintf( '%04o', $mode & 07777 ); # ? only check-for and remove world-writableness ? # if worldly if ( $mode & 0007 ) { my $newmode = $mode & ~007; # remove wordlyness my $newperm = sprintf( '%04o', $newmode & 07777 ); if ($dryrun) { print "\tLeaving mode at '$perm' as per --dryrun flag.\n"; } else { print "\tChanging $_file\'s mode from '$perm' to '$newperm'.\n" if $verbose; chmod( $newmode, $_file ) or print "\tCould not chmod() '$_file' to '$newperm': $!\n"; } } if ( $uid != $PW[2] || $gid != $PW[3] ) { warn("Ownership of '$_file' is '$uid:$gid' and it should probably be '$PW[2]:$PW[3]'."); } my $dbh = eval { Cpanel::DBI::Mysql->connect( { mysql_read_default_file => $_file }, ); }; if ($dbh) { print "\tThe file '$_file' is valid.\n" if $verbose; return 1; } print "\tThe file '$_file' is invalid:\n\t\t$@\n"; return; }; my $disable = sub { # untaint my ($_file) = $file =~ m{(.*)}; if ($dryrun) { print "\tLeaving file in place as per --dryrun flag.\n"; } else { require Cpanel::Time::ISO; # TODO: rewrite with auth data commented out my $rename_to = "$_file.$$." . Cpanel::Time::ISO::unix2iso(); if ( rename $_file => $rename_to ) { print "Successfully renamed “$_file” to “$rename_to”.\n"; } else { print "\tFailed to rename “$_file” to “$rename_to”: $!\n"; } } }; # for entries like this: operator:x:11:0:operator:/root:/sbin/nologin if ( $PW[2] != 0 && $PW[7] eq $root_home ) { print "\tnon-root user with root's homedir detected, skipping\n" if $verbose; } else { if ($justprm) { print "\tSkipping connectivity test as per --perm-only flag.\n" if $verbose; } else { if ( $PW[2] == 0 ) { my $rc = $check->(); if ( !$rc ) { _mysql_is_up_or_stop(); $disable->(); } } else { my $rc = Cpanel::AccessIds::do_as_user( $PW[0], $check ); if ( !$rc ) { _mysql_is_up_or_stop(); # detect false positive from when mysql is down, needs run as root Cpanel::AccessIds::do_as_user( $PW[0], $disable ); } } } } # } # else { # print "\tCould not change into directory '$PW[7]': $!\n"; # } } else { print "\tThe file '$file' does not exist.\n" if $verbose; } print " ... Done.\n" if $verbose; } return; } _iterate_pw(); Cpanel::PwCache::PwEnt::endpwent(); sub _mysql_is_up_or_stop { die "MySQL is not available.\n" if !Cpanel::MysqlUtils::Running::is_mysql_running(); return; }