#!/usr/local/cpanel/3rdparty/bin/perl
# cpanel - scripts/fix_reseller_acls 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
package scripts::fix_reseller_acls;
use cPstrict;
use parent qw( Cpanel::HelpfulScript );
use Try::Tiny;
use Cpanel::Exception ();
use Cpanel::LoadModule ();
use Cpanel::ConfigFiles ();
use Whostmgr::ACLS::Data ();
=encoding utf8
=head1 NAME
fix_reseller_acls
=head1 DESCRIPTION
Utility to update reseller privileges and ACL lists.
=head1 SYNOPSIS
fix_reseller_acls [OPERATION] [MODE]
Operations:
--add-default-privs Add the default set of privileges.
--fix-disallow-shell Clean up the 'disallow-shell' privilege.
Modes:
--reseller [reseller] Update the specified reseller.
--all-resellers Update all resellers on the system.
--acl-list [acl-list] Update the specified ACL list.
--all-acl-lists Update all ACL lists on the system.
--help This documentation.
=head1 Operations
Specify at least one operation.
=over
=item B<--add-default-privs>
Add the default set of privileges, introduced in v68 and later, to the set of resellers
and ACLS lists specified.
acct-summary
basic-system-info
basic-whm-functions
cors-proxy-get
connected-applications
cpanel-integration
cpanel-api
create-user-session
digest-auth
generate-email-config
list-pkgs
manage-api-tokens
manage-dns-records
manage-oidc
manage-styles
mysql-info
ns-config
ssl-info
track-email
=item B<--fix-disallow-shell>
Remove the 'disallow-shell' privilege from the set of resellers and specified ACL lists.
If the C<disallow-shell> privilege is set, then the script will remove it.
If the C<disallow-shell> privilege is not set, then the script adds the C<allow-shell> privilege.
=back
=head1 Modes
Specify at least one mode.
=over
=item B<--all-resellers>
Process all of the resellers on the system. This option overrides B<--reseller>.
B<Note>: The script does not process resellers without an associated domain in this mode.
=item B<--reseller [reseller-username]>
Process the reseller specified.
Specify this option multiple times to process mutiple resellers.
=item B<--all-acl-lists>
Process all ACL lists on the system. This option overrides B<--acl-list>.
=item B<--acl-list [acl-list]>
Process the ACL list specified.
Specify this option multiple times to process mutiple ACL lists.
=back
=head1 EXAMPLES
=over
=item C<--add-default-privs --fix-disallow-shell --all-resellers>
Update the privileges for all resellers on the system to include the default privilege and clean
up the C<disallow-shell> privilege.
=item C<--add-default-privs --reseller myreseller>
Update the privileges for the I<myreseller> reseller to include the new default privileges.
=item C<--add-default-privs --fix-disallow-shell --all-acl-lists>
Update all of the ACL lists on the system to include the default privileges, and clean up the
C<disallow-shell> privilege.
=back
=cut
sub _OPTIONS {
return qw( add-default-privs fix-disallow-shell reseller=s@ all-resellers acl-list=s@ all-acl-lists );
}
__PACKAGE__->new(@ARGV)->script() unless caller();
sub script ($self) {
$self->ensure_root();
my $opts = $self->_parse_and_validate_opts();
# This only happens if there are no resellers and/or acl-lists on the system.
# In that case, there is nothing to do and we do not want to return uncleanly
# if that happens.
return unless $opts;
$self->process_users( $opts->{resellers}, $opts->{operations} ) if $opts->{resellers} && scalar @{ $opts->{resellers} };
$self->process_acl_lists( $opts->{'acl-lists'}, $opts->{operations} ) if $opts->{'acl-lists'} && scalar @{ $opts->{'acl-lists'} };
return;
}
sub process_users ( $self, $resellers_to_process_ar, $operations_hr ) { ## no critic qw(Subroutines::ProhibitManyArgs) adding prohibit due to bug with signatures
Cpanel::LoadModule::load_perl_module('Cpanel::Reseller');
Cpanel::LoadModule::load_perl_module('Whostmgr::Resellers');
# TODO: The current interfaces to the RESELLERS_FILE do not provide
# any way to do a 'mass-edit'. Depending on how slow this process is,
# we might need to implement one.
my %current_reseller_acls = Cpanel::Reseller::getresellersaclhash();
foreach my $reseller ( @{$resellers_to_process_ar} ) {
# We validated resellers beforehand, but just in case something
# changed between that check, and the getresellersaclhash call, check again.
next unless exists $current_reseller_acls{$reseller};
print "[*] Processing reseller: '$reseller'...\n";
my $to_process_hr = {
name => $reseller,
current_acls => $current_reseller_acls{$reseller},
};
$self->add_default_privs($to_process_hr) if $operations_hr->{'add-default-privs'};
$self->fix_disallow_shell($to_process_hr) if $operations_hr->{'fix-disallow-shell'};
# set_reseller_acls requires the ACLs to have a 'acl-' prefix
Whostmgr::Resellers::set_reseller_acls( $reseller, { map { 'acl-' . $_ => 1 } keys %{ $current_reseller_acls{$reseller} } } );
print "[+] Processed reseller: '$reseller'\n";
}
return;
}
sub process_acl_lists ( $self, $acl_lists_to_process_ar, $operations_hr ) { ## no critic qw(Subroutines::ProhibitManyArgs) adding prohibit due to bug with signatures
Cpanel::LoadModule::load_perl_module('Whostmgr::ACLS');
# This is required when loading Whostmgr::ACLS -- see the module for more details
Whostmgr::ACLS::init_acls();
foreach my $acl_list ( @{$acl_lists_to_process_ar} ) {
my $list_file = "$Cpanel::ConfigFiles::ACL_LISTS_DIR/$acl_list";
next unless -f $list_file;
print "[*] Processing ACL list: '$acl_list'...\n";
if ( open( my $acl_fh, '<', $list_file ) ) {
my $acls = { map { split /=/, $_, 2 } grep { !/^\s*$/ } map { s/\n//r } readline($acl_fh) };
close($acl_fh);
my $to_process_hr = {
name => $acl_list,
current_acls => $acls,
};
$self->add_default_privs($to_process_hr) if $operations_hr->{'add-default-privs'};
$self->fix_disallow_shell($to_process_hr) if $operations_hr->{'fix-disallow-shell'};
Whostmgr::ACLS::save_acl_list(
'acllist' => $acl_list,
( map { 'acl-' . $_ => 1 } grep { $acls->{$_} } keys %{$acls} )
);
print "[+] Processed ACL list: '$acl_list'\n";
}
else {
print "[!] Failed to process ACL list '$acl_list': $!\n";
}
}
return;
}
my $defaults_to_apply_hr;
sub add_default_privs ( $self, $to_process_hr ) {
$defaults_to_apply_hr //= { map { $_ => 1 } @{ Whostmgr::ACLS::Data::get_default_acls() } };
print "\t[*] Adding default privileges to '$to_process_hr->{'name'}'...\n";
%{ $to_process_hr->{'current_acls'} } = (
%{ $to_process_hr->{'current_acls'} },
%{$defaults_to_apply_hr}
);
print "\t[+] Added default privileges to '$to_process_hr->{'name'}'.\n";
return;
}
sub fix_disallow_shell ( $self, $to_process_hr ) {
print "\t[*] Fixing 'disallow-shell' privilege for '$to_process_hr->{'name'}'...\n";
my $had_disallow_shell = delete $to_process_hr->{'current_acls'}->{'disallow-shell'};
if ( !$had_disallow_shell ) {
%{ $to_process_hr->{'current_acls'} } = (
%{ $to_process_hr->{'current_acls'} },
'allow-shell' => 1,
);
}
print "\t[+] Fixed 'disallow-shell' privilege for '$to_process_hr->{'name'}'.\n";
return;
}
sub _parse_and_validate_opts ($self) {
unless ( $self->getopt('add-default-privs') || $self->getopt('fix-disallow-shell') ) {
print $self->help();
return;
}
my $resellers = $self->getopt('reseller');
my %uniq_resellers = map { $_ => 1 } @$resellers if $resellers;
my $acl_lists = $self->getopt('acl-list');
my %uniq_acl_lists = map { $_ => 1 } @$acl_lists if $acl_lists;
my $opts = {
'operations' => {
'add-default-privs' => $self->getopt('add-default-privs'),
'fix-disallow-shell' => $self->getopt('fix-disallow-shell'),
},
'all-resellers' => $self->getopt('all-resellers'),
'specified_resellers' => \%uniq_resellers,
'all-acl-lists' => $self->getopt('all-acl-lists'),
'specified_acl_lists' => \%uniq_acl_lists,
};
$opts->{'resellers'} = $self->_validate_resellers($opts);
$opts->{'acl-lists'} = $self->_validate_acl_lists($opts);
return unless $opts->{'resellers'} // $opts->{'acl-lists'};
return $opts;
}
sub _validate_resellers ( $self, $opts ) {
if ( $opts->{'all-resellers'} ) {
Cpanel::LoadModule::load_perl_module("Whostmgr::Resellers::List");
Cpanel::LoadModule::load_perl_module('Cpanel::Config::HasCpUserFile');
return [
# Skip 'resellers without a domain' when processing all resellers on the system:
# https://go.cpanel.net/how-to-create-a-whm-reseller-without-an-associated-domain
#
# These resellers are created "out of band" by editing the resellers file,
# so altering them should be left up to the server administrators.
grep { Cpanel::Config::HasCpUserFile::has_cpuser_file($_) }
keys %{ Whostmgr::Resellers::List::list() }
];
}
elsif ( my @specified_resellers = keys %{ $opts->{'specified_resellers'} } ) {
Cpanel::LoadModule::load_perl_module("Whostmgr::Resellers::Check");
if ( my @invalid_resellers = grep { !Whostmgr::Resellers::Check::is_reseller($_) } @specified_resellers ) {
die Cpanel::Exception->create_raw( "[!] Invalid resellers specified:\n" . join( "\n", map { " " x 8 . $_ } @invalid_resellers ) . "\n" )->to_string_no_id();
}
return \@specified_resellers;
}
return;
}
sub _validate_acl_lists ( $self, $opts ) {
if ( $opts->{'all-acl-lists'} ) {
if ( opendir my $dh, $Cpanel::ConfigFiles::ACL_LISTS_DIR ) {
return [ grep { $_ !~ m/^\.+$/ && -f "$Cpanel::ConfigFiles::ACL_LISTS_DIR/$_" } readdir($dh) ];
}
}
elsif ( my @specified_acl_lists = keys %{ $opts->{'specified_acl_lists'} } ) {
if ( my @invalid_acl_lists = grep { !-f "$Cpanel::ConfigFiles::ACL_LISTS_DIR/$_" } @specified_acl_lists ) {
die Cpanel::Exception->create_raw( "[!] Invalid acl-lists specified:\n" . join( "\n", map { " " x 8 . $_ } @invalid_acl_lists ) . "\n" )->to_string_no_id();
}
return \@specified_acl_lists;
}
return;
}
1;
Mr. DellatioNx196 GaLers xh3LL Backd00r 1.0, Coded By Mr. DellatioNx196 - Bogor BlackHat