├── checksum.md5 ├── readme.txt ├── checksum.sha512 ├── LICENSE.md ├── history.txt └── acctinfo /checksum.md5: -------------------------------------------------------------------------------- 1 | c1ed74034aca966230cbe76321cb142b acctinfo 2 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | 2 | This script has been moved to https://github.com/CpanelInc/tech-acctinfo 3 | 4 | 5 | -------------------------------------------------------------------------------- /checksum.sha512: -------------------------------------------------------------------------------- 1 | 59d2353f8ebfd1f594855a4df4259f955f628bec67700e572dcebf6ebaa10d69787ff3b5872ace904336b90381fb6217f21ef08b40fe65a9c559ce9567ac437f acctinfo 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This software is Copyright © 2014-2015 by cPanel, Inc. 2 | 3 | THE SOFTWARE LICENSED HEREUNDER IS PROVIDED "AS IS" AND CPANEL HEREBY DISCLAIMS 4 | ALL WARRANTIES OF ANY KIND, WHETHER EXPRESS OR IMPLIED, RELATING TO THE SOFTWARE, 5 | ITS THIRD PARTY COMPONENTS, AND ANY DATA ACCESSED THEREFROM, OR THE ACCURACY, 6 | TIMELINESS, COMPLETENESS, OR ADEQUACY OF THE SOFTWARE, ITS THIRD PARTY COMPONENTS, 7 | AND ANY DATA ACCESSED THEREFROM, INCLUDING THE IMPLIED WARRANTIES OF TITLE, 8 | MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE, AND 9 | NON-INFRINGEMENT. CPANEL DOES NOT WARRANT THAT THE SOFTWARE OR ITS THIRD PARTY 10 | COMPONENTS ARE ERROR-FREE OR WILL OPERATE WITHOUT INTERRUPTION. IF THE SOFTWARE, 11 | ITS THIRD PARTY COMPONENTS, OR ANY DATA ACCESSED THEREFROM IS DEFECTIVE, YOU ASSUME 12 | THE SOLE RESPONSIBILITY FOR THE ENTIRE COST OF ALL REPAIR OR INJURY OF ANY KIND, EVEN 13 | IF CPANEL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DEFECTS OR DAMAGES. NO ORAL OR 14 | WRITTEN INFORMATION OR ADVICE GIVEN BY CPANEL, ITS AFFILIATES, LICENSEES, DEALERS, 15 | SUB-LICENSORS, AGENTS OR EMPLOYEES SHALL CREATE A WARRANTY OR IN ANY WAY. 16 | 17 | -------------------------------------------------------------------------------- /history.txt: -------------------------------------------------------------------------------- 1 | 2 | ############################################################# 3 | #### MOVED TO https://github.com/CpanelInc/tech-acctinfo #### 4 | ############################################################# 5 | 6 | V2.4.82- Fix cruft check. Was not finding domains in /etc/named.conf and httpd.conf files 7 | - Thanks Rex 8 | V2.4.82- Add check for reserved usernames 9 | V2.4.82- Add default reseller privileges (now marked with [DEFAULT]) 10 | 11 | V2.4.81- Fix logic bug in skipEA4 check - caused php information to not display if no CageFS. 12 | 13 | V2.4.80- Fix bug in --scan. Scanned wrong directory path. 14 | 15 | V2.4.79- Fix regexp bug in --listaddons, --listsubs --listaliased that might cause account bleed. 16 | (reported by H. Borresen) 17 | 18 | V2.4.78- Fix big bug in new listssl section if SSL's are missing. 19 | 20 | V2.4.77- Update --listssl to accommodate new location for SSL's in 68. 21 | V2.4.77- Add /etc/mailips and /etc/mailhelo to --mail section 22 | V2.4.77- Add /etc/ssldomains to --cruft check 23 | V2.4.77- Add pending SSL's for purchased orders (not just AutoSSL) 24 | V2.4.77- Updated readme.txt file 25 | 26 | V2.4.76- Fix check for /etc/vdomainaliases 27 | V2.4.76- Fix cruft check for /etc/named.conf file. 28 | V2.4.76- Add /etc/email_send_limits and /etc/ssldomains to cruft check 29 | 30 | V2.4.75- Add check for integration links Note if they are found. 31 | V2.4.75- Check to see if /etc/vdomainaliases/domain.tld exists when running --mail check. 32 | 33 | V2.4.74- Add CAA record check to --listssl 34 | V2.4.74- Add DMARC record check to --mail 35 | V2.4.74- Add check for missing password hash in /etc/shadow 36 | V2.4.74- Fix grep for cruft check and parked/addon/sub domains which might return invalid data. 37 | V2.4.74- Fix UID_MIN/GID_MIN setting to use values from /etc/login.defs (they were hard coded) 38 | 39 | V2.4.73- Add a --nocode option to skip displaying and code blocks for ticket notes. 40 | 41 | V2.4.72- Add code tags to output (for easier ticket pasting) 42 | V2.4.72- Add SANs to certificates 43 | V2.4.72- Add check on feature list to make sure AutoSSL is enabled at feature list. 44 | V2.4.72- Add AutoSSL provider check. 45 | 46 | V2.4.71- Fix grep that was taking too long to complete searching for suPHP_ConfigPath in .htaccess 47 | V2.4.71- Fix HOMEMATCH search in cruft check if HOMEMATCH is not defined it should skip. 48 | 49 | V2.4.70- A couple of small bug fixes that were really bugging me! 50 | 51 | V2.4.69- Fix Mail Routing check (again) 52 | V2.4.69- Add check for multiple homrdirs in cruft check - Thanks Ausaf! 53 | 54 | V2.4.68- Fix grep for Mail Routing (added word boundary) 55 | V2.4.68- Fix loaded configuration file (php.ini) if suPHP_ConfigPath is defined 56 | V2.4.68- Add Disk Quota and Bandwidth Used 57 | V2.4.68- Add DocumentRoot to main domain. 58 | V2.4.68- Code cleanup (colors) 59 | 60 | V2.4.67- Add bandwidth used calculation via uapi if account is over bandwidth limit. 61 | V2.4.67- Change pending autossl check to use username instead of domain name. 62 | 63 | V2.4.66- Fix --scan option to check for existence of username before moving forward, otherwise 64 | it was scanning all of /home 65 | V2.4.66- Add DocumentRoot to addon/sub/aliased domains 66 | V2.4.66- Add pending AutoSSL orders to --listssl 67 | 68 | V2.4.65- Fix CPVersion check (again) 69 | V2.4.65- Remove redundant /etc/localdomains and /etc/remotedomains checks if listed in both files. 70 | 71 | V2.4.64- Add check for theme being x3 and warn if found - can cause UI to not load. 72 | V2.4.64- Use a curl command to grab CloudFlare IP's. No longer maintained in array 73 | V2.4.64- Add more info to reverse DNS updates. Show what it's reversing to (MX, HOSTNAME). 74 | 75 | V2.4.63- Add PHP info for CloudLinux - list which has precedence. 76 | V2.4.62- Add more data to output (max ftp/sql/maillist/pop/addon/sub/parked/backups/autossl) 77 | V2.4.61- Bug Fix - Email addresses containing a period would not display. 78 | V2.4.60- Add reseller shared IP and IP Delegation info (if applicable). 79 | V2.4.59- Fix doveadm quota get command (output stopped working) 80 | V2.4.58- Add -maxdepth 1 to php.ini/.user.ini find command 81 | V2.4.57- Add --skipfind switch to skip searching for user level php.ini/.user.ini files. 82 | V2.4.56- Add additional php information if EA4 PHP version is set to inherit 83 | V2.4.55- Add more information to --mail output (suspended outgoing/hold outgoing etc...) 84 | V2.4.54- Fix CloudLinux PHP Version to not display if CageFS is Disabled, 85 | Made over quota check more informative. 86 | V2.4.53- Add check for custom php.ini, .user.ini files in users home directory. Also add check 87 | for presence of suPHP_ConfigPath in .htaccess files. 88 | V2.4.52- Fix PHP Version check for addon domains 89 | Add php.ini config file information for EA4 90 | Add timestamp to scan with Elapsed time 91 | 92 | v2.4.51- Add spinner to --scan option, exclude mail directory (may add an option to scan that 93 | in the future), clean up scanning code and add colors. 94 | 95 | V2.4.50- Skip any SSL certs not installed. Add a check for expired SSL's and warn if expired 96 | 97 | V2.4.49- Added --useDig switch to skip the Net::DNS setting. Without --useDig, it uses Net::DNS 98 | (see version 2.4.47 below) 99 | 100 | V2.4.49- Rework the Quota check - made it a little faster and CloudLinux fault tolerent. 101 | 102 | V2.4.48- Add simple scan (--scan) to scan for known infection strings. 103 | Add check for demo mode and if bandwidth limit may have been exceeded 104 | 105 | V2.4.47- Revert use of Net::DNS on obtaining A record. Seems to be getting the incorrect 106 | information from time to time. Not sure yet why. 107 | 108 | V2.4.46- Add --noquota check which sometimes causes the quota check to fail on cagefs systems. 109 | 110 | V2.4.45- Add email suspended / email receiving suspended checks 111 | 112 | V2.4.44- Add check for various SPF policy flags, fix email quota rounding 113 | 114 | V2.4.43- Add Net::DNS and Socket code to streamline the A record and MX record check. 115 | 116 | V2.4.40- Add SSL issuer (or warn if self-signed) to ssl check. 117 | Improve quota check for mail. 118 | Add check for MX reverse if it doesn't reverse back to MX record - give warning. 119 | Improve SSL check to display if an SSL Certificate is NOT installed. 120 | 121 | V2.4.40- Add SSL issuer (or warn if self-signed) to ssl check. Improve quota check for mail. 122 | Add check for MX reverse if it doesn't reverse back to MX record - give warning. 123 | 124 | V2.4.39- Add quota info as well as max defer, max emails and mailbox format info to --mail 125 | output. change listresellers switch to reselleraccts. Add --resellerperms switch 126 | 127 | V2.4.38- Fixed bug in EA4 php version check. It was returning an empty string "" 128 | 129 | V2.4.37- Fixed some minor bug fixes and ran through perltidy. 130 | 131 | V2.4.36- Add SPF/DKIM to --mail check, add CloudLinux PHP version if available. 132 | 133 | V2.4.35- Rework/clean the cruft check and add MySQL databases to cruft check. 134 | 135 | V2.4.34- Fix SSL function (was stippring out the TLD for some odd reason). 136 | 137 | V2.4.33- Fix logic error with --mail function (oops) 138 | 139 | V2.4.32- Reworked phpversion (default and account for EA4) 140 | V2.4.32- Reworked SSL certificate check (much smaller now) 141 | V2.4.32- Code clean up 142 | 143 | V2.4.31- Add /etc/valiases and /etc/vfilters to cruft check (Thanks Kyle). 144 | 145 | V2.4.29- Redirect quota STDERR output to /dev/null 146 | 147 | V2.4.28- Fix --mail option to ignore uid:gid in ~/etc/domain.tld/passwd file. 148 | 149 | V2.4.27- Add --mail option. Displays email accounts, aliases, filters, mx records, and 150 | does syntax checking of email routing (/etc/{local,remote}domains), passwd and 151 | shadow files. Still going to add SPF/DKIM info. 152 | 153 | V2.4.26- Fix php version check for EA4 (patch provided by salt-lick) - Thanks! 154 | 155 | V2.4.25- Revamped quota check. Didn't like the first method I had. 156 | 157 | V2.4.24- Fix php version check for EA4 (was changed at some time). 158 | 159 | V2.4.23- Fix quota check if Cannot stat() mounted device error. 160 | 161 | V2.4.22- Added check for custom style (PaperLantern theme) 162 | 163 | V2.4.21- Added quota check and warn if account is over quota. 164 | 165 | V2.4.20- Added permissions check of /home/USERNAME directory to make sure it is 711. 166 | 167 | v2.4.19- Added to cruft check a count of DNS lines in /var/cpanel/users/username file. 168 | 169 | V2.4.17- Fixed SSL info display and added check for uid/gid less than 500 on CentOS 5/6. 170 | 171 | V2.4.16- Added PHP Version check (if EA4) 172 | 173 | V2.4.15- Added CageFS check (if CageFS is installed). 174 | 175 | V2.4.14- Reworked the SSL certificate check (now uses /home/user/ssl/certs) 176 | V2.4.14- Fixed last login IP to only pull the last IP address 177 | 178 | V2.4.11- Added CentOS/CloudLinux Version 7 check - for MINUID less than 1000 179 | Added check for /var/cpanel/databases/username.yaml/json (11.50 uses .json) 180 | 181 | V2.4.10- Fixed empty string in cruft check that was sometimes causing missing data to show as 182 | exists. 183 | 184 | V2.4.9- Removed exit from cruft check if no data found in /var/cpanel/accounting.log 185 | which would allow the cruft check to continue. 186 | 187 | V2.4.8- Completed cruft check (Yay!) - and did a few more bug fixes. 188 | 189 | V2.4.7- Fixed small bug when domain name can not resolve (it was displaying the dig 190 | output with connection timed out) 191 | 192 | V2.4.6- Continued on cruft check - still need to check postgresql databases 193 | 194 | V2.4.2- Started cruft check routine - almost done. 195 | V2.4.2- Added border to end of main output. 196 | V2.4.2- Fixed Usage: display ($0 was sometimes returning /dev/fd/63 instead of acctinfo) 197 | V2.4.2- Added theme and shell information to output. 198 | 199 | V2.4 - Fixed issue with it occasionally getting the wrong real home directory from /etc/passwd 200 | Fixed issue with [Not on this server] IP resolution so that it does not display 201 | default webpage cgi 202 | 203 | V2.3 - Fixed resolved IP check to include NAT check, CloudFlare DNS check, and whether IP address 204 | is on the server or not. 205 | 206 | V2.0 - Almost a complete re-write. 207 | 208 | V1.10 - MODIFIED 10/12/2014 - Added color 209 | 210 | V1.9 - MODIFIED: 10/11/2014 - Cleaned up output. 211 | 212 | V1.8 - BUGFIX: 9/23/2014 - Fixed SSL Certificate display (some SSL certs were not being found). 213 | V1.8 - BUGFIX: 9/23/2014 - Fixed STARTDATE (was not being displayed) 214 | V1.8 - BUGFIX: 9/23/2014 - Fixed display of sub/addon/parked domains (if only 1 of either 215 | was created, it would not show them). 216 | 217 | V1.7 - BUGFIX: 8/22/2014 - PostGreSQL can be installed and not running - psql call would fail. 218 | V1.7 - MODIFIED: 8/22/2014 - Added PostGreSQL table size to output 219 | V1.7 - MODIFIED: 8/22/2014 - Added [Resolves to SAME] if domain resolves to same server IP. 220 | 221 | V1.6 - BUGFIX: 7/27/2014 - if PostGreSQL is not installed the psql command would fail 222 | 223 | v1.5 - MODIFIED: 7/14/2014 - Added postgresql databases and tables. 224 | 225 | v1.4 - MODIFIED: 7/14/2014 - Added "use strict" and set all variables and arrays accordingly. 226 | 227 | v1.3 - MODIFIED: 6/01/2014 - Added --help switch to list the options. (Idea by Citizen Kepler) 228 | v1.3 - MODIFIED: 6/01/2014 - Added a count of all accounts in /etc/trueuserdomains. 229 | 230 | v1.2 - MODIFIED: 5/18/2014 - Added additional switches --listsubs, --listaddons, 231 | --listparked, --listreseller 232 | 233 | v1.1 - MODIFIED: 5/7/2014 - Added --listdbs & --listssls options to list any MySQL DB's 234 | and SSL's (Idea by Tristan Wallace) 235 | v1.1 - MODIFIED: 5/8/2014 - Added -q switch to clear the screen (default is to not clear 236 | the screen) (Idea by Ryan Robson) 237 | 238 | v1.0 - CREATED: 03/28/2014 239 | -------------------------------------------------------------------------------- /acctinfo: -------------------------------------------------------------------------------- 1 | #!/usr/local/cpanel/3rdparty/bin/perl 2 | # SCRIPT: acctinfo # 3 | # PURPOSE: Get as much information for a username or domain entered at command line # 4 | # as possible. # 5 | # AUTHOR: Peter Elsner # 6 | ####################################################################################### 7 | 8 | print < \$listdbs, 89 | 'listssls' => \$listssls, 90 | 'listsubs' => \$listsubs, 91 | 'listaddons' => \$listaddons, 92 | 'listaliased' => \$listaliased, 93 | 'listparked' => \$listaliased, 94 | 'reselleraccts' => \$reselleraccts, 95 | 'resellerperms' => \$resellerperms, 96 | 'resellerprivs' => \$resellerperms, 97 | 'all' => \$all, 98 | 'help' => \$helpME, 99 | 'useDig' => \$useDig, 100 | 'cruft' => \$cruft, 101 | 'mail' => \$mail, 102 | 'scan' => \$scan, 103 | 'skipquota' => \$skipquota, 104 | 'skipfind' => \$skipfind, 105 | 'q' => \$clearscreen, 106 | 'nocode' => \$nocodeblock, 107 | ); 108 | 109 | if ($skipquota) { 110 | $skipquota = 1; 111 | } 112 | if ($skipfind) { 113 | $skipfind = 1; 114 | } 115 | 116 | if ($clearscreen) { 117 | system("clear"); 118 | } 119 | print "\n" unless ($nocodeblock); 120 | print BOLD BLUE "acctinfo - Version: " . YELLOW $VERSION . "\n"; 121 | if ($helpME) { 122 | &Usage; 123 | } 124 | 125 | my $conf = Cpanel::Config::LoadWwwAcctConf::loadwwwacctconf(); 126 | my $HOMEDIR = $conf->{'HOMEDIR'}; 127 | my $HOMEMATCH = $conf->{'HOMEMATCH'}; 128 | my $SERVER_IP = $conf->{'ADDR'}; 129 | my $cpconf = Cpanel::Config::LoadCpConf::loadcpconf(); 130 | my $DBPrefix = $cpconf->{'database_prefix'}; 131 | 132 | my ( $os_release, $os_ises ) = get_release_version(); 133 | 134 | my $IS_USERNAME = 1; 135 | 136 | my $QUERY = @ARGV[0]; 137 | chomp($QUERY); 138 | if ( $QUERY eq "" ) { 139 | &Usage; 140 | } 141 | $QUERY = lc($QUERY); 142 | if ( index( $QUERY, '.' ) != -1 ) { 143 | $IS_USERNAME = 0; 144 | } 145 | 146 | my $HOSTNAME = Cpanel::Sys::Hostname::gethostname(); 147 | my $MAINDOMAIN = ""; 148 | my $username = ""; 149 | 150 | if ($IS_USERNAME) { 151 | $MAINDOMAIN = FindMainDomain($QUERY); 152 | $username = $QUERY; 153 | } 154 | else { 155 | $username = FindUser($QUERY); 156 | $MAINDOMAIN = FindMainDomain($username); 157 | } 158 | chomp($MAINDOMAIN); 159 | chomp($username); 160 | if ( !($MAINDOMAIN) ) { 161 | $username = FindUser($QUERY); 162 | $MAINDOMAIN = FindMainDomain($username); 163 | } 164 | 165 | # If both variables are still empty, neither the username nor the domain name were found! 166 | if ( $MAINDOMAIN eq "" and $username eq "" ) { 167 | if ($cruft) { 168 | &cruft_check; 169 | } 170 | print 171 | "Error - $QUERY not found on $HOSTNAME (or missing from /etc/userdomains file)\n"; 172 | print "Try using the --cruft switch (acctinfo $QUERY --cruft)\n"; 173 | print "\n" unless ($nocodeblock); 174 | exit; 175 | } 176 | 177 | if ($scan) { 178 | &scan; 179 | exit; 180 | } 181 | 182 | if ( $username eq "nobody" ) { 183 | print RED "[WARN] - " 184 | . YELLOW $QUERY 185 | . " has an owner of " 186 | . WHITE 187 | . "\"nobody\"" 188 | . YELLOW " in /etc/userdomains!\n"; 189 | exit; 190 | } 191 | 192 | # Load /var/cpanel/users/$username into config hash variable 193 | my $user_conf = Cpanel::Config::LoadCpUserFile::load($username); 194 | my $DOMAIN = $QUERY; 195 | my $IS_PARKED = ""; 196 | my $IS_ADDON = ""; 197 | my $IS_SUB = ""; 198 | my @SUBDOMAINS = ""; 199 | my @ADDONDOMAINS = ""; 200 | my @PARKEDDOMAINS = ""; 201 | my $ACCT = ""; 202 | my $MAILBOX_FORMAT = ""; 203 | my $MAXPOP = 0; 204 | my $MAX_EMAIL_PER_HOUR = 0; 205 | my $MAX_DEFER_FAIL_PERCENTAGE = 0; 206 | open( USERDATAFILE, "/etc/userdatadomains" ); 207 | my @USERDATADOMAINS = ; 208 | close(USERDATAFILE); 209 | 210 | # Get all sub domains (if any) 211 | foreach $ACCT (@USERDATADOMAINS) { 212 | chomp($ACCT); 213 | if ( $ACCT =~ m/ $username=/ ) { 214 | if ( $ACCT =~ m/==sub==/ ) { 215 | my ($sub_domain) = ( split( /\s+/, $ACCT ) )[0]; 216 | chop($sub_domain); 217 | push( @SUBDOMAINS, $sub_domain ); 218 | if ( $sub_domain eq $DOMAIN ) { 219 | 220 | # Get documentroot for $DOMAIN from /etc/userdatadomains 221 | my ($docroot) = 222 | ( split( /==/, qx[ grep $sub_domain /etc/userdatadomains ] ) ) 223 | [4]; 224 | $IS_SUB = 225 | "$DOMAIN is a sub domain of $MAINDOMAIN \n\t\\_ " 226 | . YELLOW 227 | . "(DocumentRoot: " 228 | . WHITE $docroot . ")\n"; 229 | } 230 | } 231 | } 232 | } 233 | 234 | # Get all addon domains (if any) 235 | foreach $ACCT (@USERDATADOMAINS) { 236 | chomp($ACCT); 237 | if ( $ACCT =~ m/ $username=/ ) { 238 | if ( $ACCT =~ m/==addon==/ ) { 239 | my ($addon_domain) = ( split( /\s+/, $ACCT ) )[0]; 240 | chop($addon_domain); 241 | push( @ADDONDOMAINS, $addon_domain ); 242 | if ( $addon_domain eq $DOMAIN ) { 243 | 244 | # Get documentroot for $DOMAIN from /etc/userdatadomains 245 | my ($docroot) = ( 246 | split( 247 | /==/, qx[ grep $addon_domain /etc/userdatadomains ] 248 | ) 249 | )[4]; 250 | $IS_ADDON = 251 | "$DOMAIN is an addon domain of $MAINDOMAIN \n\t\\_ " 252 | . YELLOW 253 | . "(DocumentRoot: " 254 | . WHITE $docroot . ")\n"; 255 | } 256 | } 257 | } 258 | } 259 | 260 | # Get all aliased domains (if any) 261 | foreach $ACCT (@USERDATADOMAINS) { 262 | chomp($ACCT); 263 | if ( $ACCT =~ m/ $username=/ ) { 264 | if ( $ACCT =~ m/==parked==/ ) { 265 | my ($parked_domain) = ( split( /\s+/, $ACCT ) )[0]; 266 | chop($parked_domain); 267 | push( @PARKEDDOMAINS, $parked_domain ); 268 | if ( $parked_domain eq $DOMAIN ) { 269 | 270 | # Get documentroot for $DOMAIN from /etc/userdatadomains 271 | my ($docroot) = ( 272 | split( 273 | /==/, qx[ grep $parked_domain /etc/userdatadomains ] 274 | ) 275 | )[4]; 276 | $IS_PARKED = 277 | "$DOMAIN is an alias (parked) domain of $MAINDOMAIN \n\t\\_ " 278 | . YELLOW 279 | . "(DocumentRoot: " 280 | . WHITE $docroot . ")\n"; 281 | } 282 | } 283 | } 284 | } 285 | 286 | shift @SUBDOMAINS; 287 | shift @ADDONDOMAINS; 288 | shift @PARKEDDOMAINS; 289 | my $subcnt = @SUBDOMAINS; 290 | my $addoncnt = @ADDONDOMAINS; 291 | my $parkcnt = @PARKEDDOMAINS; 292 | 293 | if ($cruft) { 294 | &cruft_check; 295 | } 296 | 297 | # We already have the $user_conf hash variable. 298 | my $PACKAGE = $user_conf->{'PLAN'}; 299 | my $THEME = $user_conf->{'RS'}; 300 | my $IPADDR = $user_conf->{'IP'}; 301 | my $HAS_AUTOSSL = $user_conf->{'FEATURE-AUTOSSL'}; 302 | my $BACKUPENABLED = $user_conf->{'BACKUP'}; 303 | my $LEGACYBACKUP = $user_conf->{'LEGACY_BACKUP'}; 304 | my $BWLIMIT = $user_conf->{'BWLIMIT'}; 305 | my $MAXADDON = $user_conf->{'MAXADDON'}; 306 | my $MAXPARK = $user_conf->{'MAXPARK'}; 307 | my $MAXSUB = $user_conf->{'MAXSUB'}; 308 | my $MAXFTP = $user_conf->{'MAXFTP'}; 309 | my $MAXSQL = $user_conf->{'MAXSQL'}; 310 | my $MAXLST = $user_conf->{'MAXLST'}; 311 | my $FEATURELIST = $user_conf->{'FEATURELIST'}; 312 | my $STARTDATE = scalar localtime( $user_conf->{'STARTDATE'} ); 313 | my @ResolvedIP; 314 | my $ResolvedIP; 315 | 316 | if ( $BWLIMIT ne "unlimited" ) { 317 | $BWLIMIT = ( $BWLIMIT / 1024 ) / 1024; 318 | } 319 | 320 | if (qx[ grep 'FEATURE-AUTOSSL=0' /var/cpanel/users/$username ]) { 321 | $HAS_AUTOSSL = "Explicitly Disabled"; 322 | } 323 | else { 324 | $HAS_AUTOSSL = ($HAS_AUTOSSL) ? "Yes" : "No (But see featurelist above)"; 325 | } 326 | my $HAS_AUTOSSL_TEXT; 327 | 328 | #my $HAS_AUTOSSL_TEXT = "(AutoSSL enabled in \"$FEATURELIST\" feature list)"; 329 | 330 | # Check disabled feature list to see if autossl=0 exists 331 | my $AutoSSL_Disabled = qx[ grep 'autossl=0' /var/cpanel/features/disabled ]; 332 | if ($AutoSSL_Disabled) { 333 | $HAS_AUTOSSL_TEXT = "(AutoSSL in disabled feature list)"; 334 | } 335 | 336 | # Check $FEATURELIST feature list to see if autossl=0 exists 337 | my $AutoSSL_Disabled; 338 | if ( !( -e ("/var/cpanel/features/$FEATURELIST") ) ) { 339 | $AutoSSL_Disabled = "$FEATURELIST not found in /var/cpanel/features!"; 340 | $HAS_AUTOSSL_TEXT = RED "[WARN] - Missing from /var/cpanel/features/"; 341 | } 342 | else { 343 | $AutoSSL_Disabled = 344 | qx[ grep 'autossl=0' /var/cpanel/features/$FEATURELIST ]; 345 | if ($AutoSSL_Disabled) { 346 | $HAS_AUTOSSL_TEXT = "(AutoSSL disabled in $FEATURELIST)"; 347 | } 348 | } 349 | if ( $HAS_AUTOSSL_TEXT eq "" ) { 350 | $HAS_AUTOSSL_TEXT = "(AutoSSL enabled in \"$FEATURELIST\" feature list)"; 351 | } 352 | 353 | $BACKUPENABLED = ($BACKUPENABLED) ? "Yes" : "No"; 354 | $LEGACYBACKUP = ($LEGACYBACKUP) ? "Yes" : "No"; 355 | if ($IS_USERNAME) { 356 | 357 | # Resolve the ip address of $MAINDOMAIN to see if it is pointing somewhere else 358 | if ($useDig) { 359 | @ResolvedIP = qx[ dig \@208.67.220.220 $MAINDOMAIN A +short ]; 360 | } 361 | else { 362 | @ResolvedIP = getArecords($MAINDOMAIN); 363 | } 364 | } 365 | else { 366 | # a domain name was entered so we should resolve the IP of that instead. 367 | if ($useDig) { 368 | @ResolvedIP = qx[ dig \@208.67.222.222 $DOMAIN A +short ]; 369 | } 370 | else { 371 | @ResolvedIP = getArecords($DOMAIN); 372 | } 373 | } 374 | my $IPTYPE = ""; 375 | if ( $IPADDR eq $SERVER_IP ) { 376 | $IPTYPE = "shared"; 377 | } 378 | else { 379 | $IPTYPE = "dedicated"; 380 | } 381 | my $REAL_OWNER = $user_conf->{'OWNER'}; 382 | my $RO_TEXT = ""; 383 | if ( $REAL_OWNER ne $username and $REAL_OWNER ne "root" ) { 384 | $RO_TEXT = " (Which is under the reseller: $REAL_OWNER)"; 385 | } 386 | 387 | # Check if main domain (username) is a reseller. 388 | my @ACCTSOWNEDBYRESELLER = undef; 389 | my @SORTEDRESELLERACCTS = undef; 390 | my @LISTOFACCTS = undef; 391 | my $Is_Reseller = 0; 392 | my $ResellerAcctsCnt = 0; 393 | my $ResellerDomain = ""; 394 | my $vcu_account = ""; 395 | my $ResellersAcct = ""; 396 | my $RESELLER = ""; 397 | my $FOUND = ""; 398 | my $ResellerSharedIP = "None"; 399 | my @ResellerIPS = undef; 400 | my $ResellerIP = "None"; 401 | my $ResellerIPS = "None"; 402 | my @ALL_RESELLERS = Cpanel::ResellerFunctions::getresellerslist(); 403 | unshift @ALL_RESELLERS, 'root'; 404 | 405 | foreach $RESELLER (@ALL_RESELLERS) { 406 | chomp($RESELLER); 407 | if ( $RESELLER eq $username ) { 408 | $Is_Reseller = 1; 409 | 410 | # Grab resellers shared IP (if configured) from /var/cpanel/mainips/$RESELLER. 411 | if ( -e ("/var/cpanel/mainips/$RESELLER") ) { 412 | $ResellerSharedIP = qx[ cat /var/cpanel/mainips/$RESELLER ]; 413 | chomp($ResellerSharedIP); 414 | } 415 | if ( -e ("/var/cpanel/dips/$RESELLER") ) { 416 | open( RESELLERIPS, "/var/cpanel/dips/$RESELLER" ); 417 | @ResellerIPS = ; 418 | close(RESELLERIPS); 419 | } 420 | 421 | # Read all accounts in /var/cpanel/users into array 422 | opendir( ACCTS, "/var/cpanel/users" ); 423 | my @LISTOFACCTS = readdir(ACCTS); 424 | closedir(ACCTS); 425 | foreach $vcu_account (@LISTOFACCTS) { 426 | chomp($vcu_account); 427 | if ( $vcu_account =~ m/HASH/ 428 | or $vcu_account eq "." 429 | or $vcu_account eq ".." ) 430 | { 431 | next; 432 | } 433 | $FOUND = ""; 434 | $FOUND = 435 | qx[ grep 'OWNER=$username' /var/cpanel/users/$vcu_account ]; 436 | if ($FOUND) { 437 | $ResellersAcct = 438 | qx[ grep 'DNS=' /var/cpanel/users/$vcu_account ]; 439 | $ResellerDomain = substr( $ResellersAcct, 4 ); 440 | chomp($ResellerDomain); 441 | push( @ACCTSOWNEDBYRESELLER, "$ResellerDomain ($vcu_account)" ); 442 | } 443 | } 444 | $ResellerAcctsCnt = @ACCTSOWNEDBYRESELLER; 445 | $ResellerAcctsCnt--; 446 | last; 447 | } 448 | } 449 | 450 | my $TOTAL_DOMAINS = qx[ cat /etc/trueuserdomains | wc -l ]; 451 | chomp($TOTAL_DOMAINS); 452 | print WHITE "There are " 453 | . YELLOW $TOTAL_DOMAINS 454 | . WHITE " total accounts on (" 455 | . GREEN ON_BLACK $HOSTNAME 456 | . WHITE ").\n"; 457 | if ($IS_USERNAME) { 458 | print "\n"; 459 | } 460 | else { 461 | print GREEN ON_BLACK "\nThe user name for " 462 | . BLUE $DOMAIN 463 | . GREEN ON_BLACK " is: " 464 | . YELLOW $username . "\n"; 465 | } 466 | 467 | # Get home directory from /etc/passwd 468 | my @PASSWDS = undef; 469 | our $RealHome = ""; 470 | our $SSLProvider = getSSLprovider(); 471 | our $passline = ""; 472 | my $RealShell = ""; 473 | my $UID = ""; 474 | my $GID = ""; 475 | open( PASSWD, "/etc/passwd" ); 476 | @PASSWDS = ; 477 | close(PASSWD); 478 | 479 | foreach $passline (@PASSWDS) { 480 | chomp($passline); 481 | if ( $passline =~ m/\b$username\b/ ) { 482 | ($UID) = ( split( /:/, $passline ) )[2]; 483 | ($GID) = ( split( /:/, $passline ) )[3]; 484 | ($RealHome) = ( split( /:/, $passline ) )[5]; 485 | ($RealShell) = ( split( /:/, $passline ) )[6]; 486 | last; 487 | } 488 | } 489 | 490 | # Check for missing hash in /etc/shadow 491 | my ($PWHash) = ( split( /:/, qx[ grep $username /etc/shadow ] ) )[1]; 492 | if ( $PWHash eq "" ) { 493 | print RED "[WARN] * $username is missing password hash in /etc/shadow\n"; 494 | } 495 | 496 | # Get IP address from .lastlogin file (if it exists) 497 | my $LastLoginIP = ""; 498 | if ( -e ("$RealHome/.lastlogin") ) { 499 | $LastLoginIP = qx[ tail -1 $RealHome/.lastlogin ]; 500 | chomp($LastLoginIP); 501 | } 502 | 503 | print GREEN ON_BLACK "The main domain is " 504 | . YELLOW $MAINDOMAIN 505 | . GREEN ON_BLACK $RO_TEXT . "\n"; 506 | 507 | if ( $MAINDOMAIN eq $HOSTNAME ) { 508 | print RED "[WARN] - $MAINDOMAIN is the same as hostname $HOSTNAME!\n"; 509 | } 510 | if ( $QUERY eq $HOSTNAME ) { 511 | print RED "[WARN] - $QUERY is the same as hostname $HOSTNAME!\n"; 512 | } 513 | 514 | # Get docroot for MAINDOMAIN too. 515 | my ($maindocroot) = 516 | ( split( /==/, qx[ grep '^$MAINDOMAIN' /etc/userdatadomains ] ) )[4]; 517 | print "\t\\_ " . YELLOW . "(DocumentRoot: " . WHITE $maindocroot . ")\n"; 518 | 519 | print WHITE "Real Home Directory (/etc/passwd): " . CYAN $RealHome ; 520 | 521 | # Perm check - make sure $RealHome is 711 - Warn if not! 522 | if ( -e ("$RealHome") ) { 523 | my $statmode = ( stat($RealHome) )[2] & 07777; 524 | $statmode = sprintf "%lo", $statmode; 525 | if ( $statmode != 711 ) { 526 | print RED " - [WARN] - $RealHome permissions are not 711 [$statmode]\n"; 527 | print YELLOW 528 | "(Seeing require_files: permission denied errors in /var/log/exim_mainlog? - This may be why)\n"; 529 | } 530 | } 531 | print "\n"; 532 | 533 | # Check if user is in demo mode 534 | my $InDemo = qx[ grep $username /etc/demousers /etc/demodomains]; 535 | if ($InDemo) { 536 | print RED "[WARN] - $username is in demo mode!\n"; 537 | } 538 | 539 | # Check if bandwidth limit exceeded 540 | if ( -e ("/var/cpanel/bwlimited/$username") 541 | or -e ("/var/cpanel/bwlimited/$MAINDOMAIN") 542 | or -e ("/var/cpanel/bwlimited/$QUERY") ) 543 | { 544 | print RED 545 | "[WARN] - $MAINDOMAIN ($username) may have exceeded their bandwidth limit!\n"; 546 | my ($bwused) = 547 | qx[ uapi --user=$username StatsBar get_stats display=bandwidthusage | grep 'count:' | grep -v '_count' ]; 548 | chomp($bwused); 549 | my ($bwmax) = 550 | qx[ uapi --user=$username StatsBar get_stats display=bandwidthusage | grep 'max:' | grep -v '_max' ]; 551 | chomp($bwmax); 552 | $bwused =~ s/count://g; 553 | $bwmax =~ s/max://g; 554 | $bwused = alltrim($bwused); 555 | $bwmax = alltrim($bwmax); 556 | print "\t \\_ $bwused / $bwmax\n"; 557 | } 558 | if ($Is_Reseller) { 559 | print GREEN ON_BLACK "This account is also a reseller!\n"; 560 | if ($ResellerSharedIP) { 561 | print GREEN "Reseller's Shared IP: " . WHITE $ResellerSharedIP . "\n"; 562 | } 563 | if (@ResellerIPS) { 564 | print GREEN "Reseller's IP Delegation\n"; 565 | foreach $ResellerIP (@ResellerIPS) { 566 | chomp($ResellerIP); 567 | if ( $ResellerIP ne "" ) { 568 | print YELLOW "\t \\_ $ResellerIP\n"; 569 | } 570 | else { 571 | print YELLOW "\t \\_ Open Delegation " 572 | . CYAN 573 | . "(any IP on this server can be used by " 574 | . WHITE $username 575 | . CYAN . ")\n"; 576 | } 577 | } 578 | } 579 | else { 580 | print GREEN "Reseller's IP Delegation: "; 581 | print YELLOW "Open Delegation " 582 | . CYAN 583 | . "(any IP on this server can be used by " 584 | . WHITE $username 585 | . CYAN . ")\n"; 586 | } 587 | } 588 | print GREEN "$IS_PARKED\n" unless ( $IS_PARKED eq "" ); 589 | print GREEN "$IS_ADDON\n" unless ( $IS_ADDON eq "" ); 590 | print GREEN "$IS_SUB\n" unless ( $IS_SUB eq "" ); 591 | print WHITE "Shell: " . CYAN $RealShell . "\n"; 592 | &ChkForIntegration; 593 | 594 | # check if user is in /etc/ftpusers 595 | my $ftpblock = ""; 596 | if ( -e ("/etc/ftpusers") ) { 597 | $ftpblock = qx[ grep $username /etc/ftpusers ]; 598 | if ($ftpblock) { 599 | print RED 600 | "[WARN] - $username found in /etc/ftpusers file (FTP authentication will fail!\n"; 601 | } 602 | } 603 | print WHITE "UID/GID: " . CYAN $UID . "/" . $GID; 604 | my $UID_MIN = qx[ grep 'UID_MIN' /etc/login.defs ]; 605 | my $GID_MIN = qx[ grep 'GID_MIN' /etc/login.defs ]; 606 | ($UID_MIN) = ( split( /\s+/, $UID_MIN ) )[1]; 607 | ($GID_MIN) = ( split( /\s+/, $GID_MIN ) )[1]; 608 | $UID_MIN = alltrim($UID_MIN); 609 | $GID_MIN = alltrim($GID_MIN); 610 | 611 | #if ( version_compare( $os_release, qw( > 6.9 ) ) ) { 612 | if ( $UID < $UID_MIN or $GID < $GID_MIN ) { 613 | print RED " - [WARN] - UID/GID is less than $UID_MIN/$GID_MIN\n"; 614 | } 615 | else { 616 | print "\n"; 617 | } 618 | 619 | # New get quota info from uapi 620 | #my ($quotaused) = 621 | #qx[ uapi --user=$username StatsBar get_stats display=diskusage | grep 'count:' | grep -v '_count' ]; 622 | #chomp($quotaused); 623 | #my ($maxquota) = 624 | #qx[ uapi --user=$username StatsBar get_stats display=diskusage | grep 'max:' | grep -v '_max' ]; 625 | #chomp($maxquota); 626 | #$quotaused =~ s/count://g; 627 | #$maxquota =~ s/max://g; 628 | #$quotaused = alltrim($quotaused); 629 | #$maxquota = alltrim($maxquota); 630 | # Even newer, use whmapi1 (seems to be faster) 631 | my $quotaused = 632 | alltrim(qx[ whmapi1 accountsummary user=$username | grep 'diskused:' ]); 633 | ($quotaused) = ( split( /\s+/, $quotaused ) )[1]; 634 | my $maxquota = 635 | alltrim(qx[ whmapi1 accountsummary user=$username | grep 'disklimit' ]); 636 | ($maxquota) = ( split( /\s+/, $maxquota ) )[1]; 637 | print "Disk Quota: $quotaused used of $maxquota allowed\n"; 638 | 639 | # Check if account is over quota, warn if so, unless --skipquota is passed. 640 | # -s human-readable, -l local only (ignore NFS), -q quiet 641 | # check if the account is over quota (slq does this, if nothing returned, it's not over quota. 642 | if ( $skipquota == 0 ) { 643 | open( QUOTACHK, "/usr/bin/quota -slq $username 2> /dev/null |" ); 644 | my @quotachk = ; 645 | close(QUOTACHK); 646 | my $BlockLimitReached; 647 | my $quotaused; 648 | my $quotaallowed; 649 | foreach $BlockLimitReached (@quotachk) { 650 | chomp($BlockLimitReached); 651 | if ( $BlockLimitReached =~ m/Block limit reached on / ) { 652 | print RED "[WARN] - $username is over quota "; 653 | my ($quotadev) = ( split( /\s+/, $BlockLimitReached ) )[5]; 654 | my $quotaHR1 = qx[ grep $quotadev /etc/mtab ]; 655 | my ($quotaHR) = ( split( /\s+/, $quotaHR1 ) )[1]; 656 | ( $quotaused, $quotaallowed ) = ( 657 | split( 658 | /\s+/, 659 | qx[ /usr/bin/quota -sl $username | egrep -v 'Filesystem|quotas' | tail -1 ] 660 | ) 661 | )[ 2, 3 ]; 662 | print 663 | "($quotadev [$quotaHR] is over quota [$quotaused/$quotaallowed])\n"; 664 | } 665 | } 666 | } 667 | 668 | if ( !( -e ("/var/cpanel/features/$FEATURELIST") ) ) { 669 | print YELLOW 670 | "[INFO] - Skipping bandwidth check! Feature list \"$FEATURELIST\" missing from /var/cpanel/features/\n"; 671 | } 672 | else { 673 | my ($bwused) = 674 | qx[ uapi --user=$username StatsBar get_stats display=bandwidthusage | grep 'count:' | grep -v '_count' ]; 675 | chomp($bwused); 676 | my ($bwmax) = 677 | qx[ uapi --user=$username StatsBar get_stats display=bandwidthusage | grep 'max:' | grep -v '_max' ]; 678 | chomp($bwmax); 679 | $bwused =~ s/count://g; 680 | $bwmax =~ s/max://g; 681 | $bwused = alltrim($bwused); 682 | $bwmax = alltrim($bwmax); 683 | print "Bandwidth: $bwused used of $bwmax allowed\n"; 684 | } 685 | 686 | # Check for custom style (Paper Lantern Theme) 687 | my $custom_style_path = "$RealHome/var/cpanel/styled/current_style"; 688 | my $custom_style_link; 689 | my $custom_style; 690 | my @custom_style_array; 691 | my $custom_style_array; 692 | if ( -e ("$custom_style_path") ) { 693 | $custom_style_link = readlink($custom_style_path); 694 | @custom_style_array = split( "\/", $custom_style_link ); 695 | $custom_style = $custom_style_array[-1]; 696 | } 697 | 698 | print WHITE "Hosting Package: " 699 | . CYAN $PACKAGE 700 | . WHITE " (" 701 | . "Feature List: " 702 | . GREEN $FEATURELIST 703 | . WHITE ") " 704 | . $HAS_AUTOSSL_TEXT . "\n"; 705 | my $X3WARN = ""; 706 | if ( $THEME eq "x3" ) { 707 | $X3WARN = RED 708 | "[WARN] - x3 Theme deprecated. cPanel UI not loading? This is probably why!"; 709 | } 710 | if ($custom_style) { 711 | print WHITE "Theme: " 712 | . CYAN $THEME 713 | . " (Style: $custom_style) " 714 | . $X3WARN . "\n"; 715 | } 716 | else { 717 | print WHITE "Theme: " . CYAN $THEME . " " . $X3WARN . "\n"; 718 | } 719 | print WHITE "Max Addon/Alias/Sub Domains: " 720 | . CYAN $MAXADDON 721 | . WHITE " / " 722 | . CYAN $MAXPARK 723 | . WHITE " / " 724 | . CYAN $MAXSUB . "\n"; 725 | print WHITE "Max SQL Databases: " . CYAN $MAXSQL . "\n"; 726 | print WHITE "Max Mailman Lists: " . CYAN $MAXLST . "\n"; 727 | print WHITE "Max FTP Accounts: " . CYAN $MAXFTP . "\n"; 728 | print WHITE "Max Bandwidth Allowed: " . CYAN $BWLIMIT . " MB\n"; 729 | print WHITE "AutoSSL Enabled: " . CYAN $HAS_AUTOSSL . "\n"; 730 | print WHITE "Backup Enabled: " 731 | . CYAN $BACKUPENABLED 732 | . GREEN " / " 733 | . WHITE "LEGACY: " 734 | . CYAN $LEGACYBACKUP . "\n"; 735 | 736 | my $PHPDefaultVersion; 737 | my $PHPversion; 738 | my $isEA4 = 0; 739 | my $cageFSStats = &check_for_cagefs(); 740 | if ($cageFSStats) { 741 | print WHITE "CageFS: " . CYAN $cageFSStats . "\n"; 742 | 743 | # See if /home/$username/.cl.selector/defaults.cfg is present. If so grab PHP Version from it. 744 | } 745 | else { 746 | print WHITE "CageFS: " . CYAN . "Not installed!\n"; 747 | } 748 | 749 | # Check for php-selector (CloudLinux) 750 | my $clPHPVer = 0; 751 | if ( -e ("$RealHome/.cl.selector/defaults.cfg") and $cageFSStats eq "Enabled" ) 752 | { 753 | my $clPHP = qx[ egrep '^php' $RealHome/.cl.selector/defaults.cfg ]; 754 | $clPHPVer =~ s/\s+//g; 755 | ($clPHPVer) = ( split( /=/, $clPHP ) )[1]; 756 | chomp($clPHPVer); 757 | $clPHPVer = alltrim($clPHPVer); 758 | if ($clPHPVer) { 759 | print WHITE "CloudLinux PHP Version: " . CYAN $clPHPVer . "\n"; 760 | 761 | my $PHPiniFile = 762 | qx[ su -s /bin/bash $username -c "php -i | grep '^Configuration File'" ]; 763 | my $PHPiniLoad = 764 | qx[ su -s /bin/bash $username -c "php -i | grep '^Loaded Configuration File'" ]; 765 | my $PHPiniScan = 766 | qx[ su -s /bin/bash $username -c "php -i | grep '^Scan this dir'" ]; 767 | chomp($PHPiniFile); 768 | chomp($PHPiniLoad); 769 | chomp($PHPiniScan); 770 | if ( $cageFSStats and $cageFSStats eq "Enabled" ) { 771 | print YELLOW "\t \\_ $PHPiniFile\n"; 772 | print YELLOW "\t \\_ $PHPiniLoad\n"; 773 | print YELLOW "\t \\_ $PHPiniScan\n"; 774 | } 775 | } 776 | } 777 | 778 | my $skipEA4 = 0; 779 | if ( $clPHPVer and $cageFSStats eq "Enabled" ) { 780 | $skipEA4 = 1; 781 | } 782 | 783 | $isEA4 = &isEA4; 784 | my $clPHPActive = 0; 785 | if ( $isEA4 and !$skipEA4 ) { 786 | $PHPDefaultVersion = &get_system_php_version(); 787 | if ( $PHPDefaultVersion eq "" ) { 788 | $PHPDefaultVersion = "UNKNOWN"; 789 | } 790 | $PHPversion = &get_php_version(); 791 | if ( $PHPversion eq "" ) { 792 | $PHPversion = "inherit"; 793 | } 794 | if ( $PHPversion eq "inherit" ) { $clPHPActive = 1; } 795 | print WHITE "[EA4] PHP Version: " 796 | . CYAN $PHPversion 797 | . " (System Default: $PHPDefaultVersion) "; 798 | 799 | # Check for ^suPHP_ConfigPath variable in .htaccess files 800 | my $suPHPConfPathFound; 801 | if ( -e ("$RealHome/public_html/.htaccess") ) { 802 | $suPHPConfPathFound = 803 | qx[ egrep '^suPHP_ConfigPath' "$RealHome/public_html/.htaccess" ]; 804 | } 805 | 806 | if ( $clPHPActive and $cageFSStats eq "Enabled" ) { 807 | print MAGENTA "CloudLinux PHP Version has precedence\n"; 808 | } 809 | else { 810 | print MAGENTA "cPanel EA4 PHP Version has precedence\n"; 811 | } 812 | if ( -e ("/var/cpanel/userdata/$username/$MAINDOMAIN.php-fpm.yaml") ) { 813 | print YELLOW "PHP-FPM pool detected\n"; 814 | } 815 | 816 | # Get php.ini config info 817 | if ( $PHPversion eq "inherit" and $cageFSStats ne "Enabled" ) { 818 | $PHPversion = $PHPDefaultVersion; 819 | } 820 | if ( -e ("/etc/scl/conf/$PHPversion") 821 | or ( -e ("/etc/scl/prefixes/$PHPversion") ) ) 822 | { 823 | my $PHPiniFile = 824 | qx[ /usr/bin/scl enable $PHPversion "php -i" | grep '^Configuration File' ]; 825 | my $PHPiniLoad = 826 | qx[ /usr/bin/scl enable $PHPversion "php -i" | grep '^Loaded Configuration File' ]; 827 | my $PHPiniScan = 828 | qx[ /usr/bin/scl enable $PHPversion "php -i" | grep '^Scan this dir' ]; 829 | 830 | if ($suPHPConfPathFound) { 831 | print YELLOW "[NOTE]" 832 | . WHITE 833 | . " - suPHP_ConfigPath found in " 834 | . CYAN 835 | . $suPHPConfPathFound; 836 | my ($UsersuPHPConfPath) = 837 | ( split( /\s+/, $suPHPConfPathFound ) )[1]; 838 | $PHPiniFile = 839 | "Configuration File (php.ini) Path => $UsersuPHPConfPath"; 840 | $PHPiniLoad = "Loaded Configuration File => $UsersuPHPConfPath"; 841 | $PHPiniScan = "Scan this dir for additional .ini files => None"; 842 | } 843 | chomp($PHPiniFile); 844 | chomp($PHPiniLoad); 845 | chomp($PHPiniScan); 846 | print YELLOW "\t \\_ $PHPiniFile\n"; 847 | print YELLOW "\t \\_ $PHPiniLoad\n"; 848 | print YELLOW "\t \\_ $PHPiniScan\n"; 849 | } 850 | 851 | if ( !( -e ("$RealHome/public_html") ) ) { 852 | $skipfind = 1; 853 | } 854 | 855 | # Search /home/username for any php.ini files and/or .user.ini files (unless --skipfind) 856 | if ( $skipfind == 0 ) { 857 | my @customPHPINI = 858 | qx[ find $RealHome/public_html -maxdepth 1 -name 'php.ini' ]; 859 | my @customuserINI = 860 | qx[ find $RealHome/public_html -maxdepth 1 -name '.user.ini' ]; 861 | my $customPHPINIfile; 862 | my $customuserINIfile; 863 | if (@customPHPINI) { 864 | print GREEN "Custom php.ini file found in: \n"; 865 | foreach $customPHPINIfile (@customPHPINI) { 866 | chomp($customPHPINIfile); 867 | print YELLOW "\t \\_ $customPHPINIfile\n"; 868 | } 869 | } 870 | if (@customuserINI) { 871 | print GREEN "Custom .user.ini file found in: \n"; 872 | foreach $customuserINIfile (@customuserINI) { 873 | chomp($customuserINIfile); 874 | print YELLOW "\t \\_ $customuserINIfile\n"; 875 | } 876 | } 877 | print "\n"; 878 | } 879 | } 880 | 881 | my $IS_IP_ON_SERVER = qx[ ip addr | grep $IPADDR ]; 882 | my $NOTONSERVER = "[ Is configured on this server ] "; 883 | if ( $IS_IP_ON_SERVER eq "" ) { 884 | $NOTONSERVER = "[ Not configured on this server ]"; 885 | } 886 | print WHITE "IP address: " 887 | . CYAN $IPADDR 888 | . WHITE " (" 889 | . CYAN $IPTYPE 890 | . WHITE ") - $NOTONSERVER\n"; 891 | my $defaultsite = 0; 892 | my $TotalARecords = @ResolvedIP; 893 | my $ConnectionTimeout = 0; 894 | if ( @ResolvedIP[4] =~ m/no servers could be reached/ ) { 895 | $TotalARecords = 0; 896 | $ConnectionTimeout = 1; 897 | } 898 | my $ResolvesToDetail = ""; 899 | print WHITE "Resolves to IP: "; 900 | if ( $TotalARecords > 1 ) { 901 | print "(multiple A records found) \n"; 902 | foreach $ResolvedIP (@ResolvedIP) { 903 | chomp($ResolvedIP); 904 | $ResolvesToDetail = &check_resolved_ip($ResolvedIP); 905 | print CYAN "\t \\_" . $ResolvedIP . " " . RED $ResolvesToDetail . "\n"; 906 | } 907 | } 908 | else { ## ONLY 1 A RECORED RETURNED. 909 | $ResolvedIP = @ResolvedIP[0]; 910 | chomp($ResolvedIP); 911 | if ($ConnectionTimeout) { 912 | $ResolvedIP = ""; 913 | } 914 | if ( $TotalARecords == 0 and $ResolvedIP eq "" ) { 915 | print "DOES NOT RESOLVE!\n"; 916 | $defaultsite = 1; 917 | } 918 | else { 919 | $ResolvesToDetail = &check_resolved_ip($ResolvedIP); 920 | print CYAN $ResolvedIP . " " . RED $ResolvesToDetail; 921 | } 922 | print "\n"; 923 | } 924 | 925 | if ($defaultsite) { 926 | print YELLOW 927 | "Not seeing the site you're expecting (or defaultwebpage.cgi)? - This may be why!\n"; 928 | } 929 | 930 | if ( $all or $mail ) { 931 | &display_mail_info; 932 | } 933 | 934 | # Last Login IP 935 | if ($LastLoginIP) { 936 | print WHITE "Last logged in to cPanel from IP: " . CYAN $LastLoginIP . "\n"; 937 | } 938 | 939 | print WHITE "Has been a customer since " . CYAN $STARTDATE . "\n"; 940 | 941 | # Check to see if the $username is in /var/cpanel/suspended directory 942 | my $SUSP = 0; 943 | my $REASON = ""; 944 | if ( -e ("/var/cpanel/suspended/$username") ) { 945 | $REASON = `cat /var/cpanel/suspended/$username`; 946 | chomp($REASON); 947 | $SUSP = 1; 948 | } 949 | print WHITE "Suspended: "; 950 | if ($SUSP) { 951 | print RED "YES!"; 952 | print WHITE " - Reason: " . CYAN $REASON unless ( $REASON eq "" ); 953 | } 954 | else { 955 | print GREEN "No"; 956 | } 957 | print "\n"; 958 | print WHITE "Count of other domains: [" 959 | . YELLOW "SUB: " 960 | . GREEN $subcnt 961 | . WHITE "] - [" 962 | . YELLOW "ALIASES " 963 | . GREEN $parkcnt 964 | . WHITE "] - [" 965 | . YELLOW "ADDONS: " 966 | . GREEN $addoncnt 967 | . WHITE "]\n"; 968 | &border; 969 | 970 | # END OF BASIC INFORMATION (no switches were added) 971 | # ADDITIONAL INFORMATION STARTS HERE! (if all or any other switches were added) 972 | 973 | # DOMAIN INFO (ALIASES, ADDON, SUB) 974 | my $SUB = ""; 975 | my $PARK = ""; 976 | my $ADDON = ""; 977 | if ( $subcnt + $addoncnt + $parkcnt > 1 978 | and ( $all or $listsubs or $listaddons or $listparked or $listaliased ) ) 979 | { 980 | print WHITE "The following are associated with " 981 | . CYAN $MAINDOMAIN 982 | . WHITE " (" 983 | . GREEN $username 984 | . WHITE ")\n"; 985 | &smborder; 986 | } 987 | if ( $all or $listsubs ) { 988 | print YELLOW "Sub Domains: "; 989 | if ( $subcnt > 0 and ( $all or $listsubs ) ) { 990 | print "\n"; 991 | foreach $SUB (@SUBDOMAINS) { 992 | chomp($SUB); 993 | print YELLOW "\t \\_ $SUB\n"; 994 | } 995 | } 996 | else { 997 | print MAGENTA "No Sub Domains found for " 998 | . CYAN $MAINDOMAIN 999 | . WHITE " (" 1000 | . GREEN $username 1001 | . WHITE ")\n"; 1002 | } 1003 | &smborder; 1004 | } 1005 | 1006 | if ( $all or $listaddons ) { 1007 | print YELLOW "Addon Domains: "; 1008 | if ( $addoncnt > 0 and ( $all or $listaddons ) ) { 1009 | print "\n"; 1010 | foreach $ADDON (@ADDONDOMAINS) { 1011 | chomp($ADDON); 1012 | print YELLOW "\t \\_ $ADDON\n"; 1013 | } 1014 | } 1015 | else { 1016 | print MAGENTA "No Addon Domains found for " 1017 | . CYAN $MAINDOMAIN 1018 | . WHITE " (" 1019 | . GREEN $username 1020 | . WHITE ")\n"; 1021 | } 1022 | &smborder; 1023 | } 1024 | 1025 | if ( $all or $listparked or $listaliased ) { 1026 | print YELLOW "Aliased Domains: "; 1027 | if ( $parkcnt > 0 and ( $all or $listparked or $listaliased ) ) { 1028 | print "\n"; 1029 | foreach $PARK (@PARKEDDOMAINS) { 1030 | chomp($PARK); 1031 | print YELLOW "\t \\_ $PARK\n"; 1032 | } 1033 | } 1034 | else { 1035 | print MAGENTA "No Aliased Domains found for " 1036 | . CYAN $MAINDOMAIN 1037 | . WHITE " (" 1038 | . GREEN $username 1039 | . WHITE ")\n"; 1040 | } 1041 | &smborder; 1042 | } 1043 | 1044 | # RESELLER INFO 1045 | if ( $reselleraccts or $all ) { 1046 | if ($all) { &border; } 1047 | my $owned_by_reseller = ""; 1048 | if ( $Is_Reseller and $ResellerAcctsCnt > 0 ) { 1049 | print CYAN $MAINDOMAIN 1050 | . WHITE 1051 | " is a reseller and has the following ($ResellerAcctsCnt) accounts under it\n"; 1052 | shift @ACCTSOWNEDBYRESELLER; 1053 | my @SORTEDRESELLERACCTS = sort(@ACCTSOWNEDBYRESELLER); 1054 | foreach $owned_by_reseller (@SORTEDRESELLERACCTS) { 1055 | chomp($owned_by_reseller); 1056 | print BOLD YELLOW ON_BLACK "\t \\_ $owned_by_reseller\n"; 1057 | } 1058 | &border; 1059 | } 1060 | else { 1061 | print WHITE "No Reseller accounts found for " 1062 | . CYAN $MAINDOMAIN 1063 | . WHITE " (" 1064 | . GREEN $username 1065 | . WHITE ")\n"; 1066 | &border; 1067 | } 1068 | } 1069 | 1070 | if ( $resellerperms or $all ) { 1071 | if ($all) { &border; } 1072 | my $DefaultPerm = ""; 1073 | my $defaultRPerm = ""; 1074 | my @defaultperms = 1075 | qw( acct-summary basic-system-info basic-whm-functions cors-proxy-get cpanel-api cpanel-integration create-user-session digest-auth generate-email-config list-pkgs manage-api-tokens manage-dns-records manage-oidc manage-styles mysql-info ns-config public-contact ssl-info track-email ); 1076 | if ($Is_Reseller) { 1077 | open( RESELLERS, "/var/cpanel/resellers" ); 1078 | my @RESELLERS = ; 1079 | close(RESELLERS); 1080 | my $resellerline = ""; 1081 | my @rperms = undef; 1082 | my $rperm = ""; 1083 | print CYAN "The reseller " . $MAINDOMAIN 1084 | . WHITE " has the following reseller permissions\n"; 1085 | foreach $resellerline (@RESELLERS) { 1086 | chomp($resellerline); 1087 | my ( $reseller, $rperms ) = ( split( /:/, $resellerline ) ); 1088 | if ( $reseller eq $username ) { 1089 | my @rperms = split /,/, $rperms; 1090 | foreach $rperm (@rperms) { 1091 | chomp($rperm); 1092 | foreach $defaultRPerm (@defaultperms) { 1093 | chomp($defaultRPerm); 1094 | if ( $rperm =~ $defaultRPerm ) { 1095 | $DefaultPerm = BLUE ON_BLACK "[DEFAULT]"; 1096 | last; 1097 | } 1098 | else { 1099 | $DefaultPerm = ""; 1100 | } 1101 | } 1102 | print BOLD YELLOW ON_BLACK "\t \\_ $rperm " . $DefaultPerm; 1103 | if ( $rperm eq "all" ) { 1104 | print RED "[WARN] - HAS ROOT PRIVILEGES!!!\n"; 1105 | } 1106 | else { 1107 | print "\n"; 1108 | } 1109 | } 1110 | } 1111 | } 1112 | &border; 1113 | } 1114 | } 1115 | 1116 | # MySQL INFO 1117 | if ( $listdbs or $all ) { 1118 | my @USERDBS = undef; 1119 | my @DBSIZE = undef; 1120 | my $USERDB = ""; 1121 | my $SIZEOFDB = ""; 1122 | my $DBNAME = ""; 1123 | my $DBSIZE = ""; 1124 | 1125 | # MySQL username can only be the first 8 characters of $usrename 1126 | my $first8; 1127 | if ($DBPrefix) { 1128 | $first8 = substr( $username, 0, 8 ); 1129 | @USERDBS = qx[ echo "SHOW DATABASES like '$first8%'" | mysql -BN ]; 1130 | } 1131 | else { 1132 | @USERDBS = 1133 | 1134 | # THIS BELOW FAILS IF DB PREFIX IS OFF!!! 1135 | qx[ echo "SHOW DATABASES like = '$username%'" | mysql -BN ]; 1136 | } 1137 | my $DBCNT = @USERDBS; 1138 | if ( $DBCNT == 0 ) { 1139 | print WHITE "No MySQL databases found for " 1140 | . CYAN $MAINDOMAIN 1141 | . WHITE " (" 1142 | . GREEN $username 1143 | . WHITE ")\n"; 1144 | } 1145 | else { 1146 | print WHITE 1147 | "The following ($DBCNT) MySQL databases can be found under: " 1148 | . GREEN $username . "\n"; 1149 | foreach $USERDB (@USERDBS) { 1150 | chomp($USERDB); 1151 | $USERDB =~ s/\\//g; 1152 | print YELLOW "\t \\_ " . $USERDB . "\n"; 1153 | } 1154 | } 1155 | &smborder; 1156 | 1157 | # PostGreSQL INFO 1158 | my $psql_running = 0; 1159 | if (qx[ ps ax | grep postgres | grep -v grep ]) { $psql_running = 1; } 1160 | if ( -e ("/usr/bin/psql") and $psql_running ) 1161 | { ## PostGreSQL is installed and running 1162 | my @PSQLDBS = undef; 1163 | my $PgDb = ""; 1164 | @PSQLDBS = 1165 | qx[ /usr/bin/psql -U postgres -c "SELECT datname FROM pg_catalog.pg_database WHERE datistemplate='f' AND datname !='postgres'" | grep -v '\-' | grep -v 'datname' | grep -v ' row' ]; 1166 | pop(@PSQLDBS); 1167 | my $PgDbCount = @PSQLDBS; 1168 | if ( $PgDbCount == 0 ) { 1169 | print WHITE "No PostGreSQL databases found for " 1170 | . CYAN $MAINDOMAIN 1171 | . WHITE " (" 1172 | . GREEN $username 1173 | . WHITE ")\n"; 1174 | } 1175 | else { 1176 | print WHITE 1177 | "The following ($PgDbCount) PostGreSQL databases can be found under: " 1178 | . GREEN $username . "\n"; 1179 | my $pg_table = ""; 1180 | my @PG_TABLES = undef; 1181 | foreach $PgDb (@PSQLDBS) { 1182 | chomp($PgDb); 1183 | $PgDb = substr( $PgDb, 1 ); 1184 | print YELLOW "\t \\_" . $PgDb . "\n"; 1185 | } 1186 | } 1187 | } 1188 | else { 1189 | print RED "PostGreSQL server is not installed (or running) on " 1190 | . MAGENTA $HOSTNAME . "\n"; 1191 | } 1192 | &border; 1193 | } 1194 | 1195 | if ( $listssls or $all ) { 1196 | if ( $CPVersion lt "11.68.0.0" ) { 1197 | &oldlistssls; 1198 | } 1199 | else { 1200 | $sslsyscertdir = "/var/cpanel/ssl/apache_tls/"; 1201 | if ( -e ("$sslsyscertdir/$MAINDOMAIN/certificates") ) { 1202 | $sslsubject = 1203 | qx[ openssl x509 -in "$sslsyscertdir/$MAINDOMAIN/certificates" -subject -noout ]; 1204 | $startdate = 1205 | qx[ openssl x509 -in "$sslsyscertdir/$MAINDOMAIN/certificates" -startdate -noout ]; 1206 | $expiredate = 1207 | qx[ openssl x509 -in "$sslsyscertdir/$MAINDOMAIN/certificates" -enddate -noout ]; 1208 | $isSelfSigned = 1209 | qx[ openssl verify "$sslsyscertdir/$MAINDOMAIN/certificates" | grep 'self signed certificate' ]; 1210 | 1211 | print WHITE "SSL Certificates installed under " 1212 | . CYAN $MAINDOMAIN 1213 | . WHITE " (" 1214 | . GREEN $username 1215 | . WHITE ")\n"; 1216 | &dispSSLdata($MAINDOMAIN); 1217 | print WHITE "Protecting the following Subject Alternative Names:\n"; 1218 | my $SAN; 1219 | my @getSANS = 1220 | qx[ openssl x509 -in "$sslsyscertdir/$MAINDOMAIN/certificates" -noout -text | grep -oP '(?<=DNS:)[a-z0-9.-]+' ]; 1221 | 1222 | foreach $SAN (@getSANS) { 1223 | chomp($SAN); 1224 | print YELLOW "\t \\_ " . CYAN $SAN . "\n"; 1225 | } 1226 | } 1227 | else { 1228 | print YELLOW $MAINDOMAIN . "\n"; 1229 | print WHITE "\t \\_ " . CYAN . "No SSL found.\n"; 1230 | } 1231 | 1232 | foreach $SUB (@SUBDOMAINS) { 1233 | chomp($SUB); 1234 | if ( -e ("$sslsyscertdir/$SUB/certificates") ) { 1235 | $sslsubject = 1236 | qx[ openssl x509 -in "$sslsyscertdir/$SUB/certificates" -subject -noout ]; 1237 | $startdate = 1238 | qx[ openssl x509 -in "$sslsyscertdir/$SUB/certificates" -startdate -noout ]; 1239 | $expiredate = 1240 | qx[ openssl x509 -in "$sslsyscertdir/$SUB/certificates" -enddate -noout ]; 1241 | $isSelfSigned = 1242 | qx[ openssl verify "$sslsyscertdir/$SUB/certificates" | grep 'self signed certificate' ]; 1243 | &dispSSLdata($SUB); 1244 | print WHITE 1245 | "Protecting the following Subject Alternative Names:\n"; 1246 | my $SAN; 1247 | my @getSANS = 1248 | qx[ openssl x509 -in "$sslsyscertdir/$SUB/certificates" -noout -text | grep -oP '(?<=DNS:)[a-z0-9.-]+' ]; 1249 | 1250 | foreach $SAN (@getSANS) { 1251 | chomp($SAN); 1252 | print YELLOW "\t \\_ " . CYAN $SAN . "\n"; 1253 | } 1254 | } 1255 | else { 1256 | print YELLOW $SUB . "\n"; 1257 | print WHITE "\t \\_ " . CYAN . "No SSL found.\n"; 1258 | } 1259 | } 1260 | 1261 | # Check for pending AutoSSL orders here (uses whmapi1 get_autossl_pending_queue (62.0.26+ only) 1262 | if ( $SSLProvider eq "cPanel (Powered by Comodo)" ) { 1263 | print "\nChecking for pending AutoSSL orders: \n"; 1264 | my $SSL_PENDING = 1265 | qx[ whmapi1 get_autossl_pending_queue | grep -B3 'user: $username' ]; 1266 | if ($SSL_PENDING) { 1267 | my $NEW_SSL_PENDING; 1268 | ( $NEW_SSL_PENDING = $SSL_PENDING ) =~ 1269 | s/order_item_id: /order_item_id: https:\/\/manage2.cpanel.net\/certificate.cgi\?oii=/g; 1270 | $SSL_PENDING = $NEW_SSL_PENDING; 1271 | print GREEN "\t \\_ \n"; 1272 | print GREEN "$SSL_PENDING"; 1273 | } 1274 | else { 1275 | print GREEN "\t \\_ None"; 1276 | } 1277 | print "\n"; 1278 | } 1279 | 1280 | # Check for purchased SSL's 1281 | print "Checking for pending SSL Orders (non-autossl): \n"; 1282 | if ( -e ("$RealHome/.cpanel/ssl/pending_queue.json") ) { 1283 | my ($PendingSSLOrder) = ( 1284 | split( 1285 | /\s+/, 1286 | qx[ python -mjson.tool $RealHome/.cpanel/ssl/pending_queue.json|grep -A1 cPStore ] 1287 | ) 1288 | )[3]; 1289 | $PendingSSLOrder =~ s/\"//g; 1290 | $PendingSSLOrder =~ s/,//g; 1291 | $PendingSSLOrder =~ s/://g; 1292 | print CYAN "\t \\_ Pending order number: " . GREEN 1293 | . "https://manage2.cpanel.net/certificate.cgi?oii=$PendingSSLOrder\n"; 1294 | } 1295 | else { 1296 | print GREEN "\t \\_ None\n"; 1297 | } 1298 | 1299 | # Check for CAA records here. 1300 | my @HasCAA; 1301 | print "Checking for CAA records: \n"; 1302 | 1303 | #my @HasCAA = qx[ dig $MAINDOMAIN caa +short ]; 1304 | if ($IS_USERNAME) { 1305 | @HasCAA = qx[ dig +noall +answer $MAINDOMAIN CAA | grep -v CNAME ]; 1306 | } 1307 | else { 1308 | @HasCAA = qx[ dig +noall +answer $QUERY CAA | grep -v CNAME ]; 1309 | } 1310 | my $CAARecord; 1311 | if (@HasCAA) { 1312 | if ($IS_USERNAME) { 1313 | print YELLOW 1314 | "[NOTE] * CAA records were found for $MAINDOMAIN\n"; 1315 | } 1316 | else { 1317 | print YELLOW "[NOTE] * CAA records were found for $QUERY\n"; 1318 | } 1319 | print GREEN 1320 | "SSL Certificates can only be issued from the following CA's:\n"; 1321 | my ( $CAARecord1, $CAARecord2, $CAARecord3 ); 1322 | foreach $CAARecord (@HasCAA) { 1323 | chomp($CAARecord); 1324 | ( $CAARecord1, $CAARecord2, $CAARecord3 ) = 1325 | ( split( /\s+/, $CAARecord ) )[ 4, 5, 6 ]; 1326 | print CYAN "\t \\_ $CAARecord1 $CAARecord2 $CAARecord3\n"; 1327 | } 1328 | } 1329 | else { 1330 | print GREEN "\t \\_ None\n"; 1331 | } 1332 | &border; 1333 | } 1334 | } 1335 | 1336 | print "\n" unless ($nocodeblock); 1337 | exit; 1338 | 1339 | sub Usage { 1340 | print WHITE "\nUsage: " 1341 | . CYAN "acctinfo" 1342 | . WHITE " [options] domainname.tld or cPUsername [options]\n\n"; 1343 | print YELLOW "Examples: \n" 1344 | . CYAN "acctinfo" 1345 | . WHITE " --listdbs somedomain.net\n"; 1346 | print GREEN 1347 | "\t Lists any MySQL databases (and their sizes) as well as any PostGreSQL\n\t databases for somedomain.net\n\n"; 1348 | print CYAN "acctinfo" . WHITE " --listsubs cptestdo\n"; 1349 | print GREEN "\t Lists all sub domains under the cptestdo user name.\n\n"; 1350 | print CYAN "acctinfo" . WHITE " --listaddons cptestdomain.net\n"; 1351 | print GREEN 1352 | "\t Lists all addon domains under the cptestdomain.net domain name.\n\n"; 1353 | print CYAN "acctinfo" . WHITE " --listalias cptestdomain.net\n"; 1354 | print GREEN 1355 | "\t Lists all alias (parked) domains under the cptestdomain.net domain name.\n\n"; 1356 | print CYAN "acctinfo" . WHITE " --reselleraccts cptestdo\n"; 1357 | print GREEN 1358 | "\t Lists reseller information and domains under the cptestdo user name.\n\n"; 1359 | print CYAN "acctinfo" . WHITE " --resellerperms cptestdo\n"; 1360 | print GREEN 1361 | "\t Lists reseller permissions under the cptestdo user name.\n\n"; 1362 | print CYAN "acctinfo" . WHITE " --listssls cptestdomain.net\n"; 1363 | print GREEN 1364 | "\t Lists any SSL's under the cptestdomain.net domain name.\n\n"; 1365 | print CYAN "acctinfo" . WHITE " --cruft cptestdomain.net\n"; 1366 | print GREEN "\t Perform a cruft check on cptestdomain.net.\n\n"; 1367 | print CYAN "acctinfo" . WHITE " --mail cptestdomain.net\n"; 1368 | print GREEN "\t Display mail information for cptestdomain.net.\n\n"; 1369 | print CYAN "acctinfo" . WHITE " --scan cptest\n"; 1370 | print GREEN "\t Scan users home directory for known infection strings.\n\n"; 1371 | print CYAN "acctinfo" . WHITE " --all cptestdomain.net\n"; 1372 | print GREEN "\t Lists everything for the cptestdomain.net domain name.\n\n"; 1373 | print CYAN "acctinfo" . WHITE " --help\n"; 1374 | print GREEN 1375 | "\t Shows this usage information. (NOTE: [options] can go before or after domain/username).\n\n"; 1376 | exit; 1377 | } 1378 | 1379 | sub border { 1380 | print MAGENTA ON_BLACK 1381 | "==============================================================================================\n"; 1382 | return; 1383 | } 1384 | 1385 | sub smborder { 1386 | print MAGENTA 1387 | "----------------------------------------------------------------------------------------------\n"; 1388 | return; 1389 | } 1390 | 1391 | sub FindMainDomain() { 1392 | $SearchFor = $_[0]; 1393 | my $MAINUSER = ""; 1394 | my $TrueUserLine = ""; 1395 | open( TRUEUSER, "/etc/trueuserdomains" ); 1396 | my @TRUEUSERS = ; 1397 | close(TRUEUSER); 1398 | foreach $TrueUserLine (@TRUEUSERS) { 1399 | chomp($TrueUserLine); 1400 | ( $MAINDOMAIN, $MAINUSER ) = ( split( /:\s+/, $TrueUserLine ) ); 1401 | if ( $MAINUSER eq $SearchFor ) { 1402 | return $MAINDOMAIN; 1403 | } 1404 | } 1405 | } 1406 | 1407 | sub FindUser() { 1408 | my $SearchFor = $_[0]; 1409 | my $UserIsReserved = isUserReserved($SearchFor); 1410 | if ($UserIsReserved) { 1411 | print RED "[WARNING] - " 1412 | . $SearchFor 1413 | . WHITE " is a reserved username!\n"; 1414 | } 1415 | my $UserLine = ""; 1416 | my $TheDOMAIN = ""; 1417 | my $TheUSER = ""; 1418 | open( USERDOMAIN, "/etc/userdomains" ); 1419 | my @USERDOMAINS = ; 1420 | close(USERDOMAIN); 1421 | foreach $UserLine (@USERDOMAINS) { 1422 | chomp($UserLine); 1423 | ( $TheDOMAIN, $TheUSER ) = ( split( /:\s+/, $UserLine ) ); 1424 | if ( $TheDOMAIN eq $SearchFor ) { 1425 | return $TheUSER; 1426 | } 1427 | } 1428 | } 1429 | 1430 | sub check_cloudflare_ips { 1431 | my $chkIP = $_[0]; 1432 | 1433 | # Below IP's obtained from: https://www.cloudflare.com/ips 1434 | my @cf_subnets = qx[ curl -s https://www.cloudflare.com/ips-v4 ]; 1435 | my $cloudflareIP = 0; 1436 | my $cf_subnet = ""; 1437 | my @a = split /\./, $chkIP; 1438 | my $di = getIp(@a); 1439 | foreach $cf_subnet (@cf_subnets) { 1440 | ( $a, $b ) = getNetwork($cf_subnet); 1441 | if ( ( $di >= $a ) && ( $di <= $b ) ) { $cloudflareIP = 1; } 1442 | } 1443 | 1444 | sub getIp { 1445 | return ( $_[0] * 256 * 256 * 256 ) + 1446 | ( $_[1] * 256 * 256 ) + 1447 | ( $_[2] * 256 ) + 1448 | $_[3]; 1449 | } 1450 | 1451 | sub getNetwork { 1452 | @a = split( /[\/|\.]/, +shift ); 1453 | return ( getIp( @a[ 0 .. 3 ] ), 1454 | ( getIp( @a[ 0 .. 3 ] ) + ( 2**( 32 - $a[4] ) ) ) ); 1455 | } 1456 | return $cloudflareIP; 1457 | } 1458 | 1459 | sub check_for_nat { 1460 | return if ( !( -e ("/var/cpanel/cpnat") ) ); 1461 | my $chkIP = $_[0]; 1462 | open( CPNAT, "/var/cpanel/cpnat" ); 1463 | my @CPNAT = ; 1464 | close(CPNAT); 1465 | my $cpnat; 1466 | foreach $cpnat (@CPNAT) { 1467 | chomp($cpnat); 1468 | my ( $outsideIP, $insideIP ) = ( split( /\s+/, $cpnat ) ); 1469 | chomp($outsideIP); 1470 | chomp($insideIP); 1471 | if ( $outsideIP eq $chkIP ) { 1472 | return $insideIP; 1473 | } 1474 | if ( $insideIP eq $chkIP ) { 1475 | return $outsideIP; 1476 | } 1477 | } 1478 | } 1479 | 1480 | sub check_resolved_ip { 1481 | my $IP2CHK = $_[0]; 1482 | my $RetVal = ""; 1483 | if ( $IP2CHK eq $IPADDR ) { 1484 | $RetVal = GREEN . " [SAME]"; 1485 | } 1486 | else { 1487 | $defaultsite = 1; 1488 | } 1489 | my $Is_IP_OnServer = qx[ ip addr | grep '$IP2CHK' ]; 1490 | if ( !($Is_IP_OnServer) ) { 1491 | $RetVal = $RetVal .= RED . " [Not on this server]"; 1492 | $defaultsite = 1; 1493 | } 1494 | my $IS_CLOUDFLARE = check_cloudflare_ips($IP2CHK); 1495 | if ($IS_CLOUDFLARE) { 1496 | $RetVal = " <-- CloudFlare DNS"; 1497 | $defaultsite = 1; 1498 | } 1499 | my $IS_NAT = &check_for_nat($IP2CHK); 1500 | if ($IS_NAT) { 1501 | $RetVal = " NAT detected ($IS_NAT => $IP2CHK)"; 1502 | if ( $IS_NAT eq $IPADDR ) { 1503 | $RetVal = $RetVal .= GREEN . " [SAME]"; 1504 | $defaultsite = 0; 1505 | } 1506 | } 1507 | my $Is_IP_OnServer = qx[ ip addr | grep '$IS_NAT' ]; 1508 | if ( !($Is_IP_OnServer) ) { 1509 | $RetVal = $RetVal .= RED . " [Not on this server]"; 1510 | $defaultsite = 1; 1511 | } 1512 | return $RetVal; 1513 | } 1514 | 1515 | sub cruft_check { 1516 | &border; 1517 | print CYAN "CRUFT CHECK\n"; 1518 | &border; 1519 | my $maxwidth = 25; 1520 | my $file2search = ""; 1521 | my $TheStatus = ""; 1522 | my $spacer = 0; 1523 | my $len = 0; 1524 | my $filestatus = ""; 1525 | my $TrueUserLine = ""; 1526 | my $isTerminated = 0; 1527 | my $termdate = ""; 1528 | my @temp = undef; 1529 | my $DNSLineCnt = 0; 1530 | my $TotalDomainCnt = 0; 1531 | 1532 | # Check /var/cpanel/accounting.log file here (for CREATE and/or REMOVE lines) 1533 | # ONLY MAIN ACCT / DOMAIN is checked 1534 | print BLUE "From your query of " 1535 | . GREEN $QUERY 1536 | . BLUE " I have determined:\n"; 1537 | my $isActive; 1538 | my $is_acct; 1539 | my $check_for; 1540 | open( ACCOUNTING, "/var/cpanel/accounting.log" ); 1541 | foreach () { 1542 | @temp = split(/:/); 1543 | if ($IS_USERNAME) { 1544 | $check_for = ':' . $QUERY . '$'; 1545 | } 1546 | else { 1547 | $check_for = ':' . $QUERY . ':'; 1548 | } 1549 | if (/$check_for/) { 1550 | $is_acct = 1; 1551 | if (/CREATE/) { 1552 | $isActive = 1; 1553 | $isTerminated = 0; 1554 | chomp( $username = $temp[-1] ); 1555 | chomp( $MAINDOMAIN = $temp[-3] ); 1556 | } 1557 | if (/REMOVE/) { 1558 | chomp( $username = $temp[-1] ); 1559 | chomp( $MAINDOMAIN = $temp[-2] ); 1560 | $isActive = 0; 1561 | $isTerminated = 1; 1562 | @temp = (); 1563 | @temp = split(/:/); 1564 | $termdate = @temp[0] . ":" . @temp[1] . ":" . @temp[2]; 1565 | } 1566 | } 1567 | } 1568 | close(ACCOUNTING); 1569 | if ($isTerminated) { ## $is_acct is true if this is the main account/domain 1570 | print "$MAINDOMAIN ($username) was terminated on $termdate\n"; 1571 | } 1572 | if ($isActive) { 1573 | print 1574 | "$MAINDOMAIN ($username) is active (according to /var/cpanel/accounting.log)\n"; 1575 | if ( $addoncnt > 0 ) { 1576 | print "It has $addoncnt Addon domains\n"; 1577 | } 1578 | if ( $subcnt > 0 ) { 1579 | print "It has $addoncnt Sub domains\n"; 1580 | } 1581 | if ( $parkcnt > 0 ) { 1582 | print "It has $addoncnt Aliased domains\n"; 1583 | } 1584 | 1585 | # Add up the total number of additional domains 1586 | $TotalDomainCnt = $addoncnt + $subcnt + $parkcnt; 1587 | 1588 | # Now add one more to that for the main account 1589 | $TotalDomainCnt++; 1590 | 1591 | # Now count the number of "DNS" lines in /var/cpanel/users/$username and make sure 1592 | # it equalts $TotalDomainCnt. Warn if it does NOT! 1593 | $DNSLineCnt = qx[ grep -c '^DNS' /var/cpanel/users/$username ]; 1594 | } 1595 | 1596 | # END OF ACCOUNTING LOG CHECK 1597 | 1598 | my $useQuery = 0; 1599 | if ( !$isActive 1600 | and !$is_acct 1601 | and !$isTerminated 1602 | and !$MAINDOMAIN 1603 | and !$username ) 1604 | { 1605 | print 1606 | "No data found for your query of: $QUERY in /var/cpanel/accounting.log\n"; 1607 | print "Continuing search for $QUERY...\n"; 1608 | 1609 | # $username = $QUERY; 1610 | # $MAINDOMAIN = $QUERY; 1611 | } 1612 | if ( !$isActive ) { 1613 | my $isAddon; 1614 | my $isSub; 1615 | my $isParked; 1616 | $isAddon = 1617 | qx[ grep '^$QUERY:' /etc/userdatadomains | grep '==addon==' ]; 1618 | if ($isAddon) { 1619 | ($username) = ( split( /\s+/, $isAddon ) )[1]; 1620 | ($username) = ( split( /==/, $username ) ); 1621 | ($isAddon) = ( split( /:/, $isAddon ) ); 1622 | print 1623 | "$QUERY has an entry in /etc/userdatadomains as an Addon Domain under the " 1624 | . CYAN $username 1625 | . " user\n"; 1626 | } 1627 | $isSub = qx[ grep '^$QUERY:' /etc/userdatadomains | grep '==sub==' ]; 1628 | if ($isSub) { 1629 | ($username) = ( split( /\s+/, $isSub ) )[1]; 1630 | ($username) = ( split( /==/, $username ) ); 1631 | ($isSub) = ( split( /:/, $isSub ) ); 1632 | print 1633 | "$QUERY has an entry in /etc/userdatadomains as a Sub Domain under the " 1634 | . CYAN $username 1635 | . " user\n" 1636 | unless ($isAddon); 1637 | } 1638 | $isParked = 1639 | qx[ grep '^$QUERY:' /etc/userdatadomains | grep '==parked==' ]; 1640 | if ($isParked) { 1641 | ($username) = ( split( /\s+/, $isParked ) )[1]; 1642 | ($username) = ( split( /==/, $username ) ); 1643 | ($isParked) = ( split( /:/, $isParked ) ); 1644 | print 1645 | "$QUERY has an entry in /etc/userdatadomains as a Aliased Domain under the " 1646 | . CYAN $username 1647 | . " user\n"; 1648 | } 1649 | if ( !$MAINDOMAIN and $username ) { 1650 | ($MAINDOMAIN) = 1651 | ( split( /:/, qx[ grep '$username' /etc/trueuserdomains ] ) )[0]; 1652 | chomp($MAINDOMAIN); 1653 | } 1654 | $useQuery = ($IS_USERNAME) ? 1 : 0; 1655 | if ( $useQuery and ( $isAddon or $isSub or $isParked ) ) { 1656 | print "Checking main domain $MAINDOMAIN ($username) instead...\n"; 1657 | $QUERY = $username; 1658 | } 1659 | } 1660 | &smborder; 1661 | 1662 | my @FILES2SEARCH = qw( 1663 | /etc/passwd 1664 | /etc/group 1665 | /etc/shadow 1666 | /etc/gshadow 1667 | /etc/quota.conf 1668 | /etc/dbowners 1669 | /etc/userdomains 1670 | /etc/trueuserdomains 1671 | /etc/userdatadomains 1672 | /etc/userdatadomains.json 1673 | /etc/domainusers 1674 | /etc/trueuserowners 1675 | /etc/localdomains 1676 | /etc/remotedomains 1677 | /etc/demousers 1678 | /etc/email_send_limits 1679 | /etc/demoids 1680 | /etc/demodomains 1681 | /etc/ssldomains 1682 | /etc/apache2/conf/httpd.conf 1683 | /usr/local/apache/conf/httpd.conf 1684 | /var/cpanel/databases/users.db 1685 | ); 1686 | 1687 | foreach $file2search (@FILES2SEARCH) { 1688 | chomp($file2search); 1689 | if ( !( -s ($file2search) ) ) { 1690 | my $filestat = $file2search . " is either empty or missing"; 1691 | my $fileskip = CYAN "[SKIPPING]"; 1692 | &print_output( $filestat, $fileskip ); 1693 | next; 1694 | } 1695 | if ( $username eq "" ) { 1696 | $username = $QUERY; 1697 | } 1698 | if ( $MAINDOMAIN eq "" ) { 1699 | $MAINDOMAIN = $QUERY; 1700 | } 1701 | if ($useQuery) { 1702 | $filestatus = &check_file_existance( $file2search, $QUERY ); 1703 | } 1704 | else { 1705 | $filestatus = &check_file_existance( $file2search, $username ); 1706 | } 1707 | if ( !($filestatus) and !$useQuery ) { 1708 | $filestatus = &check_file_existance( $file2search, $MAINDOMAIN ); 1709 | } 1710 | if ($filestatus) { $filestatus = GREEN "[EXISTS]"; } 1711 | else { $filestatus = RED "[MISSING]"; } 1712 | &print_output( $file2search, $filestatus ); 1713 | } 1714 | if ($username) { 1715 | 1716 | # Check for home directory and others to see if they exist 1717 | my $hmCnt; 1718 | my $dirstatus = &check_dir("/$HOMEDIR/$username"); 1719 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1720 | else { $dirstatus = RED "[MISSING]"; } 1721 | &print_output( "$HOMEDIR/$username", $dirstatus ); 1722 | if ( $dirstatus =~ m/MISSING/ ) { 1723 | if ($HOMEMATCH) { 1724 | print "Checking other possible home directory locations...\n"; 1725 | 1726 | # Now check HOMEMATCH 1 through 9. 1727 | for ( $hmCnt = 1 ; $hmCnt < 10 ; $hmCnt = $hmCnt + 1 ) { 1728 | my $dirstatus = &check_dir("/$HOMEMATCH$hmCnt/$username"); 1729 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1730 | else { $dirstatus = RED "[MISSING]"; } 1731 | &print_output( "/$HOMEMATCH$hmCnt/$username", $dirstatus ); 1732 | } 1733 | } 1734 | } 1735 | 1736 | # Check /var/cpanel/userdata/$username 1737 | my $dirstatus = &check_dir("/var/cpanel/userdata/$username"); 1738 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1739 | else { $dirstatus = RED "[MISSING]"; } 1740 | &print_output( "/var/cpanel/userdata/$username", $dirstatus ); 1741 | 1742 | # Check main file in /var/cpanel/userdata/$username directory 1743 | my $dirstatus = &check_dir("/var/cpanel/userdata/$username/main"); 1744 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1745 | else { $dirstatus = RED "[MISSING]"; } 1746 | &print_output( "/var/cpanel/userdata/$username/main", $dirstatus ); 1747 | 1748 | # Check /var/cpanel/users/$username 1749 | my $dirstatus = &check_dir("/var/cpanel/users/$username"); 1750 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1751 | else { $dirstatus = RED "[MISSING]"; } 1752 | &print_output( "/var/cpanel/users/$username", $dirstatus ); 1753 | if ( $DNSLineCnt != $TotalDomainCnt ) { 1754 | print RED 1755 | "\t \\_ [WARN]: One or more DNS lines may be missing from this file!\n"; 1756 | } 1757 | 1758 | # Check if /var/cpanel/databases/grants_$username.yaml exists! 1759 | my $dirstatus = 1760 | &check_dir("/var/cpanel/databases/grants_$username.yaml"); 1761 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1762 | else { $dirstatus = RED "[MISSING]"; } 1763 | &print_output( "/var/cpanel/databases/grants_$username.yaml", 1764 | $dirstatus ); 1765 | 1766 | my $yaml_json = ( $CPVersion lt "11.50.0.0" ) ? "yaml" : "json"; 1767 | my $dirstatus = 1768 | &check_dir("/var/cpanel/databases/$username.$yaml_json"); 1769 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1770 | else { $dirstatus = RED "[MISSING]"; } 1771 | &print_output( "/var/cpanel/databases/$username.$yaml_json", 1772 | $dirstatus ); 1773 | my $dbindex = 1774 | ( $CPVersion lt "11.50.0.0" ) 1775 | ? "/var/cpanel/databases/dbindex.db" 1776 | : "/var/cpanel/databases/dbindex.db.json"; 1777 | my $dirstatus = &check_file_existance( $dbindex, $username ); 1778 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1779 | else { $dirstatus = RED "[MISSING]"; } 1780 | &print_output( $dbindex, $dirstatus ); 1781 | my $dirstatus = &check_dir("/etc/proftpd/$username"); 1782 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1783 | else { $dirstatus = RED "[MISSING]"; } 1784 | &print_output( "/etc/proftpd/$username", $dirstatus ); 1785 | 1786 | # Check for /var/cpanel/bandwidth/username.sqlite file. 1787 | my $dirstatus = &check_dir("/var/cpanel/bandwidth/$username.sqlite"); 1788 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1789 | else { $dirstatus = RED "[MISSING]"; } 1790 | &print_output( "/var/cpanel/bandwidth/$username.sqlite", $dirstatus ); 1791 | 1792 | } 1793 | else { 1794 | print "Could not determine username so skipping all username checks!\n"; 1795 | } 1796 | if ( !$IS_USERNAME ) { 1797 | 1798 | # Check /etc/valiases/$QUERY 1799 | my $dirstatus = &check_dir("/etc/valiases/$QUERY"); 1800 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1801 | else { $dirstatus = RED "[MISSING]"; } 1802 | &print_output( "/etc/valiases/$QUERY", $dirstatus ); 1803 | 1804 | # Check /etc/vfilters/$QUERY 1805 | my $dirstatus = &check_dir("/etc/vfilters/$QUERY"); 1806 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1807 | else { $dirstatus = RED "[MISSING]"; } 1808 | &print_output( "/etc/vfilters/$QUERY", $dirstatus ); 1809 | 1810 | # Check /var/named/$QUERY.db file 1811 | my $dirstatus = &check_dir("/var/named/$QUERY.db"); 1812 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1813 | else { $dirstatus = RED "[MISSING]"; } 1814 | &print_output( "/var/named/$QUERY.db", $dirstatus ); 1815 | 1816 | # Check /etc/named.conf file 1817 | my $dirstatus = &check_file_existance( "/etc/named.conf", $QUERY ); 1818 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1819 | else { $dirstatus = RED "[MISSING]"; } 1820 | &print_output( "/etc/named.conf", $dirstatus ); 1821 | } 1822 | else { 1823 | my $dirstatus = &check_dir("/etc/valiases/$MAINDOMAIN"); 1824 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1825 | else { $dirstatus = RED "[MISSING]"; } 1826 | &print_output( "/etc/valiases/$MAINDOMAIN", $dirstatus ); 1827 | 1828 | # Check /etc/vfilters/$MAINDOMAIN 1829 | my $dirstatus = &check_dir("/etc/vfilters/$MAINDOMAIN"); 1830 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1831 | else { $dirstatus = RED "[MISSING]"; } 1832 | &print_output( "/etc/vfilters/$MAINDOMAIN", $dirstatus ); 1833 | 1834 | # Check /var/named/$MAINDOMAIN.db file 1835 | my $dirstatus = &check_dir("/var/named/$MAINDOMAIN.db"); 1836 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1837 | else { $dirstatus = RED "[MISSING]"; } 1838 | &print_output( "/var/named/$MAINDOMAIN.db", $dirstatus ); 1839 | 1840 | # Check /etc/named.conf file 1841 | my $dirstatus = &check_file_existance( "/etc/named.conf", $MAINDOMAIN ); 1842 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1843 | else { $dirstatus = RED "[MISSING]"; } 1844 | &print_output( "/etc/named.conf", $dirstatus ); 1845 | 1846 | # Check for php-fpm conf files 1847 | my $dirstatus = 1848 | &check_dir("/opt/cpanel/ea-php*/root/etc/php-fpm.d/$MAINDOMAIN.conf"); 1849 | if ($dirstatus) { $dirstatus = GREEN "[EXISTS]"; } 1850 | else { $dirstatus = RED "[MISSING]"; } 1851 | &print_output( 1852 | "/opt/cpanel/ea-php*/root/etc/php-fpm.d/$MAINDOMAIN.conf", 1853 | $dirstatus ); 1854 | } 1855 | 1856 | # Check for DNS Clustering 1857 | if ( -e "/var/cpanel/useclusteringdns" ) { 1858 | print "Found DNS Cluster - checking...\n"; 1859 | opendir( CLUSTERS, "/var/cpanel/cluster/root/config" ); 1860 | my @DNSCLUSTERS = readdir(CLUSTERS); 1861 | closedir(CLUSTERS); 1862 | my ( $dnscluster, $QueryCluster ); 1863 | foreach $dnscluster (@DNSCLUSTERS) { 1864 | chomp($dnscluster); 1865 | if ( $dnscluster eq "." 1866 | or $dnscluster eq ".." 1867 | or $dnscluster =~ m/dnsrole/ 1868 | or $dnscluster =~ m/.cache/ ) 1869 | { 1870 | next; 1871 | } 1872 | if ($IS_USERNAME) { 1873 | $QueryCluster = qx[ dig \@$dnscluster $MAINDOMAIN +short ]; 1874 | if ($QueryCluster) { 1875 | print YELLOW "\t \\_ $MAINDOMAIN " 1876 | . GREEN ON_BLACK 1877 | . "was found in " 1878 | . YELLOW $dnscluster . "\n"; 1879 | } 1880 | else { 1881 | print YELLOW "\t \\_ $MAINDOMAIN " 1882 | . RED 1883 | . "NOT found in " 1884 | . YELLOW $dnscluster . "\n"; 1885 | } 1886 | } 1887 | else { 1888 | $QueryCluster = qx[ dig \@$dnscluster $QUERY +short ]; 1889 | if ($QueryCluster) { 1890 | print YELLOW "\t \\_ $QUERY " 1891 | . GREEN ON_BLACK 1892 | . "was found in " 1893 | . $dnscluster . "\n"; 1894 | } 1895 | else { 1896 | print YELLOW "\t \\_ $QUERY " 1897 | . RED 1898 | . "NOT found in " 1899 | . YELLOW $dnscluster . "\n"; 1900 | } 1901 | } 1902 | } 1903 | } 1904 | 1905 | # Check MySQL users table. 1906 | my @MySQLUsers = undef; 1907 | my @MySQLDBs = undef; 1908 | my $MySQLUser; 1909 | my $MySQLDB; 1910 | my $first8; 1911 | if ($DBPrefix) { 1912 | if ( length($username) > 8 ) { 1913 | $first8 = substr( $username, 0, 8 ); 1914 | } 1915 | else { 1916 | $first8 = $username; 1917 | } 1918 | } 1919 | else { 1920 | $first8 = $username; 1921 | } 1922 | 1923 | # Check for MySQL databases 1924 | my @MySQLDBs = qx[ mysql -BNe "show databases like '$first8%'" ]; 1925 | my @MySQLDBsUnique = uniq(@MySQLDBs); 1926 | my $dbnum = @MySQLDBsUnique; 1927 | if ( $dbnum > 0 ) { 1928 | print YELLOW "MySQL Databases Found\n"; 1929 | foreach $MySQLDB (@MySQLDBsUnique) { 1930 | chomp($MySQLDB); 1931 | print WHITE "\t \\_ " . $MySQLDB . "\n"; 1932 | } 1933 | } 1934 | 1935 | # Check for database users 1936 | if ( $first8 eq "" ) { 1937 | print "Skipping MySQL User check - no username found!\n"; 1938 | } 1939 | else { 1940 | my @MySQLUsers = 1941 | qx[ echo "SELECT User from mysql.user WHERE User REGEXP '$first8'" | mysql -BN ]; 1942 | my @MySQLUsersUnique = uniq(@MySQLUsers); 1943 | my $num = @MySQLUsersUnique; 1944 | if ( $num > 0 ) { 1945 | print YELLOW "MySQL Users Found in MySQL.user table\n"; 1946 | foreach $MySQLUser (@MySQLUsersUnique) { 1947 | chomp($MySQLUser); 1948 | print WHITE "\t \\_ " . $MySQLUser . "\n"; 1949 | } 1950 | } 1951 | } 1952 | 1953 | # Check for postgres 1954 | my $psql_running = 0; 1955 | if (qx[ ps ax | grep postgres | grep -v grep ]) { $psql_running = 1; } 1956 | if ( -e ("/usr/bin/psql") and $psql_running ) 1957 | { ## PostGreSQL is installed and running 1958 | my @check_postgres_users = 1959 | qx[ /usr/bin/psql -t -h localhost --username=postgres -c 'select usename from pg_user'|grep "$username" ]; 1960 | if (@check_postgres_users) { 1961 | print YELLOW "PostGreSQL Users Found in pg_user database\n"; 1962 | my @postgres_users = split( "\n", "@check_postgres_users" ); 1963 | foreach (@postgres_users) { 1964 | chomp($_); 1965 | print WHITE "\t \\_" . $_ . "\n"; 1966 | } 1967 | } 1968 | } 1969 | else { 1970 | print "\nPostGreSQL server is not installed (or running) on " 1971 | . MAGENTA $HOSTNAME . "\n"; 1972 | } 1973 | &border; 1974 | print "\n" unless ($nocodeblock); 1975 | exit; 1976 | } 1977 | 1978 | sub check_file_existance { 1979 | my $TheFile = $_[0]; 1980 | my $TheSearchString = $_[1]; 1981 | my @TheFileData = undef; 1982 | my $DataLine = ""; 1983 | 1984 | #my $FoundLine = 0; 1985 | my $FoundLine = ""; 1986 | if ( -e ($TheFile) ) { 1987 | 1988 | # File exists, open it and read into array 1989 | # open( TheFile, "$TheFile" ); 1990 | # @TheFileData = ; 1991 | # close(TheFile); 1992 | # foreach $DataLine (@TheFileData) { 1993 | # chomp($DataLine); 1994 | 1995 | #if ( $DataLine =~ m/\b$TheSearchString\b/ ) { 1996 | # if ( $DataLine =~ m/^$TheSearchString/ ) { 1997 | # $FoundLine = 1; 1998 | # last; 1999 | # } 2000 | # } 2001 | $FoundLine = qx[ grep -w '$TheSearchString' $TheFile ]; 2002 | if ($FoundLine) { return 1; } 2003 | else { return 0; } 2004 | } 2005 | } 2006 | 2007 | sub print_output { 2008 | my $DisplayName = $_[0]; 2009 | my $TheStatus = $_[1]; 2010 | my $maxwidth = 30; 2011 | my $spacer = 0; 2012 | my $len = length($DisplayName); 2013 | $spacer = ( $maxwidth - $len ) + 50; 2014 | print YELLOW "$DisplayName"; 2015 | printf "%" . $spacer . "s", $TheStatus; 2016 | print "\n"; 2017 | select( undef, undef, undef, 0.25 ); 2018 | } 2019 | 2020 | sub check_dir() { 2021 | my $Dir2Check = $_[0]; 2022 | if ( -e ($Dir2Check) ) { 2023 | return 1; 2024 | } 2025 | else { 2026 | return 0; 2027 | } 2028 | } 2029 | 2030 | sub uniq { 2031 | my %seen; 2032 | grep !$seen{$_}++, @_; 2033 | } 2034 | 2035 | # Taken from ssp - Thanks to Chris Dillon! 2036 | sub version_cmp { # should only be used by version_compare() 2037 | no warnings 'uninitialized' 2038 | ; # Prevent uninitialized value warnings when not using all 4 values 2039 | my ( $a1, $b1, $c1, $d1 ) = split /[\._]/, $_[0]; 2040 | my ( $a2, $b2, $c2, $d2 ) = split /[\._]/, $_[1]; 2041 | return $a1 <=> $a2 || $b1 <=> $b2 || $c1 <=> $c2 || $d1 <=> $d2; 2042 | } 2043 | 2044 | sub version_compare { 2045 | 2046 | # example: return if version_compare($ver_string, qw( >= 1.2.3.3 )); 2047 | # Must be no more than four version numbers separated by periods and/or underscores. 2048 | my ( $ver1, $mode, $ver2 ) = @_; 2049 | return if ( $ver1 =~ /[^\._0-9]/ ); 2050 | return if ( $ver2 =~ /[^\._0-9]/ ); 2051 | 2052 | # Shamelessly copied the comparison logic out of Cpanel::Version::Compare 2053 | my %modes = ( 2054 | '>' => sub { 2055 | return if $_[0] eq $_[1]; 2056 | return version_cmp(@_) > 0; 2057 | }, 2058 | '<' => sub { 2059 | return if $_[0] eq $_[1]; 2060 | return version_cmp(@_) < 0; 2061 | }, 2062 | '==' => sub { return $_[0] eq $_[1] || version_cmp(@_) == 0; }, 2063 | '!=' => sub { return $_[0] ne $_[1] && version_cmp(@_) != 0; }, 2064 | '>=' => sub { 2065 | return 1 if $_[0] eq $_[1]; 2066 | return version_cmp(@_) >= 0; 2067 | }, 2068 | '<=' => sub { 2069 | return 1 if $_[0] eq $_[1]; 2070 | return version_cmp(@_) <= 0; 2071 | } 2072 | ); 2073 | return if ( !exists $modes{$mode} ); 2074 | return $modes{$mode}->( $ver1, $ver2 ); 2075 | } 2076 | 2077 | # ripped from /usr/local/cpanel/Cpanel/Sys/OS.pm 2078 | sub get_release_version { 2079 | my $ises = 0; 2080 | my $ver; 2081 | 2082 | if ( open my $fh, '<', '/etc/redhat-release' ) { 2083 | my $line = readline $fh; 2084 | close $fh; 2085 | chomp $line; 2086 | if ( $line =~ m/(?:Corporate|Advanced\sServer|Enterprise)/i ) { 2087 | $ises = 1; 2088 | } 2089 | elsif ( $line =~ /CloudLinux|CentOS/i ) { $ises = 2; } 2090 | elsif ( $line =~ /WhiteBox/i ) { $ises = 3; } 2091 | elsif ( $line =~ /caos/i ) { $ises = 4; } 2092 | if ( $line =~ /(\d+\.\d+)/ ) { $ver = $1; } 2093 | elsif ( $line =~ /(\d+)/ ) { $ver = $1; } 2094 | } 2095 | 2096 | if ($ises) { 2097 | return ( $ver, $ises ); 2098 | } 2099 | else { 2100 | return ( $ver, 0 ); 2101 | } 2102 | } 2103 | 2104 | sub check_for_cagefs() { 2105 | return unless ( -e ("/usr/sbin/cagefsctl") ); 2106 | my $tcageFSStats = qx[ /usr/sbin/cagefsctl --user-status $username ]; 2107 | chomp($tcageFSStats); 2108 | return $tcageFSStats; 2109 | } 2110 | 2111 | sub get_php_version() { 2112 | return unless ($isEA4); 2113 | my $phpUserVersion; 2114 | my $userdataline; 2115 | my @USERDATA; 2116 | 2117 | # NOTE: If $IS_USERNAME, then use $MAINDOMAIN, otherwise use $QUERY. 2118 | my $tcDomain = ""; 2119 | if ( $IS_USERNAME or $QUERY eq $MAINDOMAIN ) { 2120 | $tcDomain = $MAINDOMAIN; 2121 | } 2122 | else { 2123 | ($tcDomain) = ( split( /\./, $QUERY ) )[0]; 2124 | $tcDomain = $tcDomain . "." . $MAINDOMAIN; 2125 | } 2126 | open( USERDATA, "/var/cpanel/userdata/$username/$tcDomain" ); 2127 | @USERDATA = ; 2128 | close(USERDATA); 2129 | foreach $userdataline (@USERDATA) { 2130 | if ( $userdataline =~ m/phpversion:/ ) { 2131 | ($phpUserVersion) = ( split( /: /, $userdataline ) )[1]; 2132 | chomp($phpUserVersion); 2133 | } 2134 | } 2135 | return $phpUserVersion; 2136 | } 2137 | 2138 | sub get_system_php_version() { 2139 | return unless ($isEA4); 2140 | open( PHPCONF, "/etc/cpanel/ea4/php.conf" ); 2141 | my @PHPCONF = ; 2142 | close(PHPCONF); 2143 | my $phpconfline; 2144 | my $phpDefault; 2145 | foreach $phpconfline (@PHPCONF) { 2146 | chomp($phpconfline); 2147 | if ( $phpconfline =~ m/default:/ ) { 2148 | ($phpDefault) = ( split( /: /, $phpconfline ) )[1]; 2149 | } 2150 | } 2151 | return $phpDefault; 2152 | } 2153 | 2154 | sub alltrim() { 2155 | my $string2trim = $_[0]; 2156 | $string2trim =~ s/^\s*(.*?)\s*$/$1/; 2157 | return $string2trim; 2158 | } 2159 | 2160 | sub isEA4 { 2161 | if ( -f "/etc/cpanel/ea4/is_ea4" ) { 2162 | return 1; 2163 | } 2164 | return undef; 2165 | } 2166 | 2167 | sub display_mail_info { 2168 | if ($IS_USERNAME) { 2169 | $DOMAIN = $MAINDOMAIN; 2170 | } 2171 | 2172 | # first let's get the email accounts. 2173 | my $emailacctline; 2174 | opendir( EMAILACCTS, "$RealHome/mail/$DOMAIN" ); 2175 | my @EMAILACCTS = readdir(EMAILACCTS); 2176 | closedir(EMAILACCTS); 2177 | my @SORTED2 = sort(@EMAILACCTS); 2178 | @EMAILACCTS = @SORTED2; 2179 | &smborder; 2180 | 2181 | # Check for suspended from outgoing email 2182 | my $SMTPUserSusp = qx[ grep $username /etc/outgoing_mail_suspended_users ]; 2183 | if ($SMTPUserSusp) { 2184 | print RED "[WARN] - $username is suspended from sending email\n"; 2185 | } 2186 | 2187 | # Check for hold from outgoing email 2188 | my $SMTPUserHold = qx[ grep $username /etc/outgoing_mail_hold_users ]; 2189 | if ($SMTPUserHold) { 2190 | print RED "[WARN] - $username is on hold from sending email\n"; 2191 | } 2192 | 2193 | if ( -s ("/etc/vdomainaliases/$DOMAIN") ) { 2194 | print YELLOW "[INFO] - " 2195 | . $DOMAIN 2196 | . CYAN 2197 | " listed in the /etc/vdomainaliases directory. Existing accounts/autoresponders will NOT forward!\n"; 2198 | } 2199 | 2200 | print "Email accounts for $DOMAIN: \n"; 2201 | 2202 | foreach $emailacctline (@EMAILACCTS) { 2203 | chomp($emailacctline); 2204 | next 2205 | if ( $emailacctline =~ 2206 | /^\.|^\.\.|new|cur|tmp|mailboxes|storage|maildirsize|maildirfolder|subscriptions|dovecot/ 2207 | ); 2208 | $emailacctline =~ s/\///g; ## Strip trailing / 2209 | print CYAN "\t \\_ " . $emailacctline . "\@" . $DOMAIN . " "; 2210 | 2211 | # If v58+, get quota via doveadm command. 2212 | if ( $CPVersion gt "11.58.0.0" ) { 2213 | my $quotaline = 2214 | qx[ doveadm -f tab quota get -u $emailacctline\@$DOMAIN | grep 'Mailbox' | grep -v 'MESSAGE' ]; 2215 | if ( $quotaline =~ m/Error: User doesn't exist/ ) { 2216 | print "[Quota cannot be determined]\n"; 2217 | } 2218 | else { 2219 | my ( $qused, $qlimit, $qpercent ) = 2220 | ( split( /\s+/, $quotaline ) )[ 2, 3, 4 ]; 2221 | if ( $qused == 0 ) { 2222 | $qused = 0; 2223 | } 2224 | else { 2225 | $qused = ( $qused / 1024 ); 2226 | } 2227 | if ( $qlimit eq "-" ) { 2228 | $qlimit = "Unlimited"; 2229 | } 2230 | else { 2231 | $qlimit = ( $qlimit / 1024 ); 2232 | } 2233 | print "[Quota Used " 2234 | . $qused 2235 | . " MB of " 2236 | . $qlimit . " MB (" 2237 | . $qpercent . "%)]\n"; 2238 | } 2239 | } 2240 | else { 2241 | print "\n"; 2242 | } 2243 | 2244 | # Check the passwd and shadow files to make sure an entry exists 2245 | my $upasswdline; 2246 | my $ushadowline; 2247 | my $upasswdOK = 0; 2248 | my $ushadowOK = 0; 2249 | if ( -e ("$RealHome/etc/$DOMAIN/passwd") ) { 2250 | open( UPASSWD, "$RealHome/etc/$DOMAIN/passwd" ); 2251 | my @UPASSWD = ; 2252 | close(UPASSWD); 2253 | my $upasswdstring = "$emailacctline:x:"; 2254 | foreach $upasswdline (@UPASSWD) { 2255 | chomp($upasswdline); 2256 | if ( $upasswdline =~ m/^$upasswdstring/ ) { 2257 | $upasswdOK = 1; 2258 | last; 2259 | } 2260 | } 2261 | } 2262 | else { 2263 | print RED 2264 | "[WARN] - $RealHome/etc/$DOMAIN/passwd file is missing!\n"; 2265 | $upasswdOK = 0; 2266 | next; 2267 | } 2268 | 2269 | # Check for suspended from incoming email 2270 | if ( -e ("$RealHome/etc/.$emailacctline\@$DOMAIN.suspended_incoming") ) 2271 | { 2272 | print RED "\t \t \\_ incoming email suspended\n"; 2273 | } 2274 | 2275 | # Shadow file 2276 | if ( -e ("$RealHome/etc/$DOMAIN/shadow") ) { 2277 | open( USHADOW, "$RealHome/etc/$DOMAIN/shadow" ); 2278 | my @USHADOW = ; 2279 | close(USHADOW); 2280 | my $shadowstring = "$emailacctline:!!"; 2281 | foreach $ushadowline (@USHADOW) { 2282 | chomp($ushadowline); 2283 | if ( $ushadowline =~ m/^$emailacctline/ ) { 2284 | if ( $ushadowline =~ m/^$shadowstring/ ) { 2285 | print RED "\t \t \\_ email login suspended\n"; 2286 | } 2287 | $ushadowOK = 1; 2288 | last; 2289 | } 2290 | } 2291 | } 2292 | else { 2293 | print RED 2294 | "[WARN] - $RealHome/etc/$DOMAIN/shadow file is missing!\n"; 2295 | $ushadowOK = 0; 2296 | next; 2297 | } 2298 | if ( !($upasswdOK) ) { 2299 | print RED 2300 | "\t\t \\_ [WARN] - Missing passwd entry for $emailacctline\@$DOMAIN"; 2301 | print "\n"; 2302 | } 2303 | if ( !($ushadowOK) ) { 2304 | print RED 2305 | "\t\t \\_ [WARN] - Missing shadow entry for $emailacctline\@$DOMAIN"; 2306 | print "\n"; 2307 | } 2308 | 2309 | # Check for .boxtrapperenable touch file - enabled if it exists. 2310 | if ( -e ("$RealHome/etc/$DOMAIN/$emailacctline/.boxtrapperenable") ) { 2311 | print YELLOW "\t \t \\_ Spam Boxtrapper Enabled\n"; 2312 | } 2313 | 2314 | # Check for mailbox_format.cpanel file. Display contents if it exists 2315 | if ( 2316 | -e ("$RealHome/mail/$DOMAIN/$emailacctline/mailbox_format.cpanel") ) 2317 | { 2318 | my $mbformat = 2319 | qx [ cat "$RealHome/mail/$DOMAIN/$emailacctline/mailbox_format.cpanel" ]; 2320 | chomp($mbformat); 2321 | print YELLOW "\t \t \\_ Account is using the $mbformat format.\n"; 2322 | } 2323 | 2324 | # If a filter file exists in /$RealHome/etc/$DOMAIN/$emailacctline directory, 2325 | # Count the number of filters and display that here. 2326 | # As \_ User Level Filters: nn 2327 | my $filtercnt = 0; 2328 | my $ufilter; 2329 | my @UFILTER; 2330 | my @ufiltname; 2331 | 2332 | if ( -e ("$RealHome/etc/$DOMAIN/$emailacctline/filter") ) { 2333 | open( FILTFILE, "$RealHome/etc/$DOMAIN/$emailacctline/filter" ); 2334 | my @UFILTER = ; 2335 | close(FILTFILE); 2336 | foreach $ufilter (@UFILTER) { 2337 | if ( substr( $ufilter, 0, 2 ) =~ m/# / 2338 | or substr( $ufilter, 0, 2 ) =~ m/#$/ ) 2339 | { 2340 | next; 2341 | } 2342 | if ( substr( $ufilter, 0, 1 ) eq "#" ) { 2343 | push( @ufiltname, substr( $ufilter, 1 ) ); 2344 | $filtercnt++; 2345 | } 2346 | } 2347 | if ( $filtercnt > 0 ) { 2348 | print YELLOW "\t \t \\_ has " 2349 | . $filtercnt 2350 | . " user level filters\n"; 2351 | my $listfilt; 2352 | foreach $listfilt (@ufiltname) { 2353 | chomp($listfilt); 2354 | print MAGENTA "\t\t\t \\_ Name: " . $listfilt . "\n"; 2355 | } 2356 | } 2357 | } 2358 | } 2359 | &smborder; 2360 | 2361 | # Now let's get the MX record and make sure the A record for it points to this server. 2362 | print "Checking MX records for $DOMAIN...\n"; 2363 | my @MXRecords = getMXrecord($DOMAIN); 2364 | my $myline; 2365 | my $skipMXchk = 0; 2366 | foreach $myline (@MXRecords) { 2367 | chomp($myline); 2368 | if ( $myline eq "NONE" ) { 2369 | $skipMXchk = 1; 2370 | last; 2371 | } 2372 | } 2373 | my $IsRemote = 0; 2374 | my $MXRecord; 2375 | my $Is_IP_OnServer; 2376 | if ( !$skipMXchk ) { 2377 | if (@MXRecords) { 2378 | foreach $MXRecord (@MXRecords) { 2379 | chomp($MXRecord); 2380 | my $ARecordForMX; 2381 | my @ARecordForMX = getArecords($MXRecord); 2382 | foreach $ARecordForMX (@ARecordForMX) { 2383 | chomp($ARecordForMX); 2384 | my $IS_NAT = &check_for_nat($ARecordForMX); 2385 | if ($IS_NAT) { ## NAT IP ADDRESS RETURNED! 2386 | $Is_IP_OnServer = qx[ ip addr | grep '$IS_NAT' ]; 2387 | if ($Is_IP_OnServer) { 2388 | print YELLOW 2389 | "\t \\_ $MXRecord resolves to $ARecordForMX => $IS_NAT (Configured on this server)\n"; 2390 | 2391 | # Check reverse 2392 | my $ReverseOfMX = getptr($MXRecord); 2393 | if ( $ReverseOfMX eq $MXRecord ) { 2394 | print GREEN 2395 | "\t\t \\_ [OK] - $ARecordForMX reverses back to the MX: $MXRecord\n"; 2396 | } 2397 | elsif ( $ReverseOfMX eq $HOSTNAME ) { 2398 | print GREEN 2399 | "\t\t \\_ [OK] - $ARecordForMX reverses back to the hostname: $HOSTNAME\n"; 2400 | } 2401 | elsif ( $ReverseOfMX eq "mail.$MXRecord" ) { 2402 | print GREEN 2403 | "\t\t \\_ [OK] - $ARecordForMX reverses back to: mail.$MXRecord\n"; 2404 | } 2405 | else { 2406 | if ( $ReverseOfMX eq "" ) { 2407 | $ReverseOfMX = "[NXDOMAIN]"; 2408 | } 2409 | print RED 2410 | "\t\t \\_ [WARN] - $ARecordForMX reverses back to: $ReverseOfMX\n"; 2411 | } 2412 | } 2413 | else { 2414 | print YELLOW 2415 | "\t \\_ $MXRecord resolves to $ARecordForMX (NOT configured on this server)\n"; 2416 | $IsRemote = 1; 2417 | } 2418 | } 2419 | else { ## NO NAT FOUND! 2420 | $Is_IP_OnServer = qx[ ip addr | grep '$ARecordForMX' ]; 2421 | if ($Is_IP_OnServer) { 2422 | print YELLOW 2423 | "\t \\_ $MXRecord resolves to $ARecordForMX (Configured on this server)\n"; 2424 | 2425 | # Check reverse 2426 | my $ReverseOfMX = getptr($MXRecord); 2427 | if ( $ReverseOfMX eq $MXRecord ) { 2428 | print GREEN 2429 | "\t\t \\_ [OK] - $ARecordForMX reverses back to the MX: $MXRecord\n"; 2430 | } 2431 | elsif ( $ReverseOfMX eq $HOSTNAME ) { 2432 | print GREEN 2433 | "\t\t \\_ [OK] - $ARecordForMX reverses back to the hostname $HOSTNAME\n"; 2434 | } 2435 | elsif ( $ReverseOfMX eq "mail.$MXRecord" ) { 2436 | print GREEN 2437 | "\t\t \\_ [OK] - $ARecordForMX reverses back to mail.$MXRecord\n"; 2438 | } 2439 | else { 2440 | if ( $ReverseOfMX eq "" ) { 2441 | $ReverseOfMX = "[NXDOMAIN]"; 2442 | } 2443 | print RED 2444 | "\t\t \\_ [WARN] - $ARecordForMX reverses back to $ReverseOfMX\n"; 2445 | } 2446 | } 2447 | else { 2448 | print YELLOW 2449 | "\t \\_ $MXRecord resolves to $ARecordForMX (NOT configured on this server)\n"; 2450 | $IsRemote = 1; 2451 | } 2452 | } 2453 | } 2454 | } 2455 | } 2456 | } 2457 | else { 2458 | print CYAN "\t \\_ None\n"; 2459 | } 2460 | 2461 | # Depending on whether $IsRemote is true or false, we check if the domain is listed in 2462 | # /etc/localdomains or /etc/remotedomains 2463 | &smborder; 2464 | my $IsInRemoteDomains = qx[ egrep -w '^$DOMAIN' /etc/remotedomains ]; 2465 | my $IsInLocalDomains = qx[ egrep -w '^$DOMAIN' /etc/localdomains ]; 2466 | chomp($IsInRemoteDomains); 2467 | chomp($IsInLocalDomains); 2468 | print "Checking email routing (based on MX check above)...\n"; 2469 | if ($IsRemote) { 2470 | 2471 | if ($IsInRemoteDomains) { 2472 | print GREEN 2473 | "\t \\_ [OK] - $DOMAIN is listed in /etc/remotedomains\n"; 2474 | $IsInLocalDomains = qx[ egrep -w '^$DOMAIN' /etc/localdomains ]; 2475 | if ($IsInLocalDomains) { 2476 | print RED 2477 | "\t \\_ [WARN] - $DOMAIN was found in /etc/localdomains\n"; 2478 | } 2479 | } 2480 | else { 2481 | print RED 2482 | "\t \\_ [WARN] - $DOMAIN is missing from /etc/remotedomains\n"; 2483 | if ($IsInLocalDomains) { 2484 | print RED 2485 | "\t \\_ [WARN] - $DOMAIN was found in /etc/localdomains\n"; 2486 | print YELLOW 2487 | "\t \\_ [NOTE] - OK if MX record is pointing to an external anti-spam service/gateway\n"; 2488 | } 2489 | } 2490 | } 2491 | else { ## is local 2492 | if ($IsInLocalDomains) { 2493 | print GREEN 2494 | "\t \\_ [OK] - $DOMAIN is listed in /etc/localdomains\n"; 2495 | $IsInRemoteDomains = qx[ egrep -w '^$DOMAIN' /etc/remotedomains ]; 2496 | if ($IsInRemoteDomains) { 2497 | print RED 2498 | "\t \\_ [WARN] - $DOMAIN was found in /etc/remotedomains\n"; 2499 | } 2500 | } 2501 | else { 2502 | print RED 2503 | "\t \\_ [WARN] - $DOMAIN is missing from /etc/localdomains\n"; 2504 | if ($IsInRemoteDomains) { 2505 | print RED 2506 | "\t \\_ [WARN] - $DOMAIN was found in /etc/remotedomains\n"; 2507 | } 2508 | } 2509 | } 2510 | 2511 | # Now list aliases (if any) 2512 | my $emailfwd; 2513 | my $fwdtype; 2514 | my $listfwd; 2515 | &smborder; 2516 | print "Aliases/Forwarders:\n"; 2517 | open( FORWARDERS, "/etc/valiases/$DOMAIN" ); 2518 | my @FWDS = ; 2519 | close(FORWARDERS); 2520 | 2521 | if (@FWDS) { 2522 | foreach $emailfwd (@FWDS) { 2523 | chomp($emailfwd); 2524 | if ( $emailfwd =~ m/: / ) { $fwdtype = "(normal forward/alias)"; } 2525 | if ( $emailfwd =~ m/\*:/ ) { 2526 | $fwdtype = "(deliver to $username main account)"; 2527 | } 2528 | if ( $emailfwd =~ m/:fail:/ ) { 2529 | $fwdtype = "(fail - bounce with message)"; 2530 | } 2531 | if ( $emailfwd =~ m/:blackhole:/ ) { 2532 | $fwdtype = "(blackhole - discard [not recommended])"; 2533 | } 2534 | if ( $emailfwd =~ m/\|/ ) { $fwdtype = "(pipe to program)"; } 2535 | if ( $emailfwd =~ m/autorespond/ ) { 2536 | $fwdtype = "(auto responder)"; 2537 | } 2538 | if ( $emailfwd =~ m/mailman\/mail/ ) { 2539 | $fwdtype = "(mailman list)"; 2540 | ($listfwd) = ( split( /\s+/, $emailfwd ) )[0]; 2541 | $emailfwd = $listfwd; 2542 | } 2543 | print YELLOW "\t \\_ " . $emailfwd . " " . $fwdtype . "\n"; 2544 | } 2545 | } 2546 | else { 2547 | print CYAN "\t \\_ None\n"; 2548 | } 2549 | 2550 | # get any system level filters (from /etc/vfilters/$DOMAIN file) 2551 | my $gfilter; 2552 | &smborder; 2553 | print "Global Level (system) Filters:\n"; 2554 | open( GFILTFILE, "/etc/vfilters/$DOMAIN" ); 2555 | my @GFILTER = ; 2556 | my $gfiltercnt = 0; 2557 | my @gfiltname; 2558 | close(GFILTFILE); 2559 | 2560 | if (@GFILTER) { 2561 | foreach $gfilter (@GFILTER) { 2562 | if ( substr( $gfilter, 0, 2 ) =~ m/# / 2563 | or substr( $gfilter, 0, 2 ) =~ m/#$/ ) 2564 | { 2565 | next; 2566 | } 2567 | if ( substr( $gfilter, 0, 1 ) eq "#" ) { 2568 | push( @gfiltname, substr( $gfilter, 1 ) ); 2569 | $gfiltercnt++; 2570 | } 2571 | } 2572 | print CYAN "\t \t \\_ " 2573 | . $DOMAIN . " has " 2574 | . $gfiltercnt 2575 | . " global level filters\n"; 2576 | my $glistfilt; 2577 | foreach $glistfilt (@gfiltname) { 2578 | chomp($glistfilt); 2579 | print YELLOW "\t\t\t \\_ Name: " . $glistfilt . "\n"; 2580 | } 2581 | } 2582 | else { 2583 | print CYAN "\t \\_ None\n"; 2584 | } 2585 | 2586 | # Here we add the spf record and dkim record (if they exist). 2587 | my $spf = qx[ dig \@208.67.222.222 $DOMAIN TXT +short | grep 'spf1' ]; 2588 | my $dkim = qx[ dig \@208.67.220.220 default._domainkey.$DOMAIN TXT +short ]; 2589 | my $dmarc = qx[ dig \@208.67.222.222 _dmarc.$DOMAIN TXT +short ]; 2590 | print "Checking SPF Record For $DOMAIN\n"; 2591 | if ($spf) { 2592 | print YELLOW "\t \\_ " . $spf; 2593 | if ( $spf =~ m/\+all/ ) { 2594 | print CYAN 2595 | "\t\t \\_ Pass All (Allow all email! Like not having any SPF at all)\n"; 2596 | } 2597 | if ( $spf =~ m/\-all/ ) { 2598 | print CYAN 2599 | "\t\t \\_ Hard Fail (Reject all email unless from ipv4/ipv6, mx or a)\n"; 2600 | } 2601 | if ( $spf =~ m/\~all/ ) { 2602 | print CYAN 2603 | "\t\t \\_ Soft Fail (Allow mail from anywhere, but mark as possible forgery) [DEFAULT]\n"; 2604 | } 2605 | if ( $spf =~ m/\?all/ ) { 2606 | print CYAN 2607 | "\t\t \\_ Neutral (No policy statement! Like not having any SPF at all)\n"; 2608 | } 2609 | print "\n"; 2610 | } 2611 | else { 2612 | print YELLOW "\t \\_ None\n"; 2613 | } 2614 | print "Checking DKIM Record For default._domainkey.$DOMAIN\n"; 2615 | if ($dkim) { 2616 | print YELLOW "\t \\_ " . $dkim . "\n"; 2617 | } 2618 | else { 2619 | print YELLOW "\t \\_ None\n"; 2620 | } 2621 | print "Checking DMARC Record For _dmarc.$DOMAIN\n"; 2622 | if ($dmarc) { 2623 | print YELLOW "\t \\_ " . $dmarc . "\n"; 2624 | } 2625 | else { 2626 | print YELLOW "\t \\_ None\n"; 2627 | } 2628 | 2629 | # Add MAX_EMAIL_PER_HOUR, MAX_DEFER_FAIL_PERCENTAGE, MAILBOX_FORMAT 2630 | $MAILBOX_FORMAT = $user_conf->{'MAILBOX_FORMAT'}; 2631 | $MAX_EMAIL_PER_HOUR = $user_conf->{'MAX_EMAIL_PER_HOUR'}; 2632 | $MAX_DEFER_FAIL_PERCENTAGE = $user_conf->{'MAX_DEFER_FAIL_PERCENTAGE'}; 2633 | $MAXPOP = $user_conf->{'MAXPOP'}; 2634 | print WHITE "Max Mail Accounts " . CYAN $MAXPOP . "\n"; 2635 | print WHITE "Mailbox Format: " . CYAN ucfirst($MAILBOX_FORMAT) . "\n"; 2636 | print WHITE "Max Emails Per Hour: " 2637 | . CYAN ucfirst($MAX_EMAIL_PER_HOUR) . "\n"; 2638 | print WHITE "Max Defer Fail %: " 2639 | . CYAN ucfirst($MAX_DEFER_FAIL_PERCENTAGE) . "\n"; 2640 | 2641 | # Check here if send mail from dedicated IP is set and if /etc/mailips or /etc/mailhelo is 2642 | # referenced. 2643 | my $SendFromDedicated = 2644 | qx[ grep 'per_domain_mailips=1' /etc/exim.conf.localopts ]; 2645 | $SendFromDedicated = ($SendFromDedicated) ? "Yes" : "No"; 2646 | my $CustomHelo = qx[ grep 'custom_mailhelo=1' /etc/exim.conf.localopts ]; 2647 | $CustomHelo = ($CustomHelo) ? "Yes" : "No"; 2648 | my $CustomMailIP = qx[ grep 'custom_mailips=1' /etc/exim.conf.localopts ]; 2649 | $CustomMailIP = ($CustomMailIP) ? "Yes" : "No"; 2650 | my $CustomMailIPText = ""; 2651 | 2652 | if ( $CustomMailIP eq "Yes" ) { 2653 | $CustomMailIPText = qx[ egrep '^$MAINDOMAIN' /etc/mailips ]; 2654 | chomp($CustomMailIPText); 2655 | } 2656 | my $CustomHeloText = ""; 2657 | if ( $CustomHelo eq "Yes" ) { 2658 | $CustomHeloText = qx[ egrep '^$MAINDOMAIN' /etc/mailhelo ]; 2659 | chomp($CustomHeloText); 2660 | } 2661 | print WHITE "Send from dedicated IP: " . CYAN $SendFromDedicated . "\n"; 2662 | print WHITE "Using Custom HELO (/etc/mailhelo): " 2663 | . CYAN $CustomHelo . " " 2664 | . YELLOW $CustomHeloText . "\n"; 2665 | print WHITE "Using Custom IP (/etc/mailips): " 2666 | . CYAN $CustomMailIP . " " 2667 | . YELLOW $CustomMailIPText . "\n"; 2668 | &border; 2669 | } 2670 | 2671 | sub getMXrecord { 2672 | my $tcDomain = $_[0]; 2673 | my $rr; 2674 | my @NEWMX; 2675 | my $res = Net::DNS::Resolver->new; 2676 | my @mx = mx( $res, $tcDomain ); 2677 | if (@mx) { 2678 | foreach $rr (@mx) { 2679 | push( @NEWMX, $rr->exchange ); 2680 | } 2681 | return @NEWMX; 2682 | } 2683 | else { 2684 | return "NONE"; 2685 | } 2686 | } 2687 | 2688 | sub getptr() { 2689 | my $ip = $_[0]; 2690 | chomp($ip); 2691 | my $ipaddr = inet_aton($ip); 2692 | my $ptrname = gethostbyaddr( $ipaddr, AF_INET ); 2693 | return $ptrname; 2694 | } 2695 | 2696 | sub getArecords { 2697 | my $tcDomain = $_[0]; 2698 | my @addresses = gethostbyname($tcDomain); 2699 | @addresses = map { inet_ntoa($_) } @addresses[ 4 .. $#addresses ]; 2700 | return @addresses; 2701 | } 2702 | 2703 | sub scan { 2704 | my $URL = 2705 | "https://raw.githubusercontent.com/cPanelPeter/infection_scanner/master/strings.txt"; 2706 | my @DEFINITIONS = qx[ curl -s $URL ]; 2707 | my $StringCnt = @DEFINITIONS; 2708 | 2709 | # Need to define and use $RealHome if $HOMEDIR doesn't exist!!! 2710 | open( PASSWD, "/etc/passwd" ); 2711 | @PASSWDS = ; 2712 | close(PASSWD); 2713 | foreach $passline (@PASSWDS) { 2714 | chomp($passline); 2715 | if ( $passline =~ m/\b$username\b/ ) { 2716 | ($UID) = ( split( /:/, $passline ) )[2]; 2717 | ($GID) = ( split( /:/, $passline ) )[3]; 2718 | ($RealHome) = ( split( /:/, $passline ) )[5]; 2719 | ($RealShell) = ( split( /:/, $passline ) )[6]; 2720 | last; 2721 | } 2722 | } 2723 | 2724 | print "Scanning $RealHome for ($StringCnt) known infections:\n"; 2725 | my @SEARCHSTRING = sort(@DEFINITIONS); 2726 | my @FOUND = undef; 2727 | my $SOMETHING_FOUND = 0; 2728 | my $SEARCHSTRING; 2729 | my ( $sec, $min, $hour, $mday, $mon, $year ); 2730 | my $scanstarttime = Time::Piece->new; 2731 | 2732 | print "Scan started on $scanstarttime\n"; 2733 | 2734 | foreach $SEARCHSTRING (@SEARCHSTRING) { 2735 | &spin; 2736 | chomp($SEARCHSTRING); 2737 | my $SCAN = 2738 | qx[ grep -srIl --exclude-dir=www --exclude-dir=mail --exclude-dir=tmp --exclude=*.png --exclude=*.svg --exclude-dir=access-logs -w "$SEARCHSTRING" $RealHome/* ]; 2739 | 2740 | chomp($SCAN); 2741 | if ($SCAN) { 2742 | $SOMETHING_FOUND = 1; 2743 | $SEARCHSTRING =~ s/\\//g; 2744 | print YELLOW "\n\n\bThe phrase " 2745 | . CYAN 2746 | . $SEARCHSTRING 2747 | . YELLOW 2748 | . " was found in file(s)\n"; 2749 | print GREEN 2750 | "==================================================================\n"; 2751 | print RED "$SCAN\n"; 2752 | } 2753 | } 2754 | if ( $SOMETHING_FOUND == 0 ) { 2755 | print GREEN "\bNothing suspicious found!\n"; 2756 | } 2757 | print "\n"; 2758 | my $scanendtime = Time::Piece->new; 2759 | print "Scan completed on $scanendtime\n"; 2760 | my $scantimediff = ( $scanendtime - $scanstarttime ); 2761 | print "Elapsed Time: ", $scantimediff->pretty, "\n"; 2762 | print "\n" unless ($nocodeblock); 2763 | } 2764 | 2765 | sub spin { 2766 | my %spinner = ( '|' => '/', '/' => '-', '-' => '\\', '\\' => '|' ); 2767 | $spincounter = ( !defined $spincounter ) ? '|' : $spinner{$spincounter}; 2768 | print STDERR "\b$spincounter"; 2769 | } 2770 | 2771 | sub getSSLprovider { 2772 | my $RetVal = ""; 2773 | if ( !( -e ("/var/cpanel/autossl.json") ) ) { 2774 | $RetVal = "Disabled"; 2775 | return $RetVal; 2776 | } 2777 | my $CurrentProvider = 2778 | qx[ grep '"provider":"cPanel"' /var/cpanel/autossl.json ]; 2779 | if ($CurrentProvider) { $RetVal = "cPanel (Powered by Comodo)"; } 2780 | my $CurrentProvider = 2781 | qx[ grep '"provider":"LetsEncrypt"' /var/cpanel/autossl.json ]; 2782 | if ($CurrentProvider) { $RetVal = "Let's Encrypt"; } 2783 | my $CurrentProvider = qx[ grep '"provider":null' /var/cpanel/autossl.json ]; 2784 | if ($CurrentProvider) { $RetVal = "Disabled"; } 2785 | return $RetVal; 2786 | } 2787 | 2788 | sub ChkForIntegration { 2789 | return unless ( -e "/var/cpanel/integration/dynamicui/$username" ); 2790 | print YELLOW "[NOTE] * Integration links found in " . GREEN 2791 | . "/var/cpanel/integration/dynamicui/$username\n"; 2792 | } 2793 | 2794 | sub dispSSLdata { 2795 | my $tcDomain = $_[0]; 2796 | my $CNloc = index( $sslsubject, "CN=" ); 2797 | my $CN = substr( $sslsubject, $CNloc ); 2798 | my ( $domain, $crap ) = ( split( /\//, $CN ) )[0]; 2799 | $domain = substr( $domain, 3 ); 2800 | chomp($domain); 2801 | chomp($startdate); 2802 | chomp($expiredate); 2803 | ($startdate) = ( split( /=/, $startdate ) )[1]; 2804 | ($expiredate) = ( split( /=/, $expiredate ) )[1]; 2805 | my $isExpired; 2806 | 2807 | if ( $noDateManip == 0 ) { 2808 | my $unix_time = UnixDate( ParseDate($expiredate), "%s" ); 2809 | my $time_now = qx[ date -u ]; 2810 | chomp($time_now); 2811 | my $unix_time_now = UnixDate( ParseDate($time_now), "%s" ); 2812 | $isExpired = GREEN "[VALID]"; 2813 | if ( $unix_time_now > $unix_time ) { 2814 | $isExpired = RED "[EXPIRED]"; 2815 | } 2816 | } 2817 | print YELLOW . $domain . "\n"; 2818 | print WHITE "\t \\_ Not Before: " . GREEN . $startdate . "\n"; 2819 | print WHITE "\t \\_ Not After : " 2820 | . GREEN 2821 | . $expiredate . " " 2822 | . $isExpired . "\n"; 2823 | if ($isSelfSigned) { 2824 | print RED "\t \\_ [WARN] " . WHITE "- Self-Signed Certificate\n"; 2825 | } 2826 | else { 2827 | # Get Issuer and display it. 2828 | my $SSLIssuer = 2829 | qx[ openssl x509 -in "$sslsyscertdir/$tcDomain/certificates" -issuer -noout ]; 2830 | my $Oloc = index( $SSLIssuer, "O=" ); 2831 | my $O = substr( $SSLIssuer, $Oloc ); 2832 | my ( $SSLIssuer, $crap ) = ( split( /\//, $O ) )[0]; 2833 | $SSLIssuer = substr( $SSLIssuer, 2 ); 2834 | chomp($SSLIssuer); 2835 | print GREEN "\t \\_ [CA SIGNED] " . WHITE "Issued by: $SSLIssuer\n"; 2836 | } 2837 | } 2838 | 2839 | sub oldlistssls { 2840 | my $sslcertdir = "$RealHome/ssl/certs"; 2841 | my $sslsyscertdir = "/var/cpanel/ssl/installed/certs"; 2842 | my @certs; 2843 | my $cert; 2844 | my $sslsubject; 2845 | my $startdate; 2846 | my $expiredate; 2847 | if ( -e ("$sslcertdir") ) { 2848 | print WHITE "SSL Certificates installed under " 2849 | . CYAN $MAINDOMAIN 2850 | . WHITE " (" 2851 | . GREEN $username 2852 | . WHITE ")\n"; 2853 | opendir( ssldir, "$sslcertdir" ); 2854 | my @certs = readdir(ssldir); 2855 | closedir(ssldir); 2856 | foreach $cert (@certs) { 2857 | my $SSLNotInstalled = 0; 2858 | chomp($cert); 2859 | if ( $cert eq "." 2860 | or $cert eq ".." 2861 | or ( substr( $cert, -4 ) ne '.crt' ) ) 2862 | { 2863 | next; 2864 | } 2865 | if ( !( -e ("$sslsyscertdir/$cert") ) ) { 2866 | next; 2867 | } 2868 | $sslsubject = 2869 | qx[ openssl x509 -in "$sslcertdir/$cert" -subject -noout ]; 2870 | my $CNloc = index( $sslsubject, "CN=" ); 2871 | my $CN = substr( $sslsubject, $CNloc ); 2872 | my ( $domain, $crap ) = ( split( /\//, $CN ) )[0]; 2873 | $domain = substr( $domain, 3 ); 2874 | chomp($domain); 2875 | $startdate = 2876 | qx[ openssl x509 -in "$sslcertdir/$cert" -startdate -noout ]; 2877 | $expiredate = 2878 | qx[ openssl x509 -in "$sslcertdir/$cert" -enddate -noout ]; 2879 | chomp($startdate); 2880 | chomp($expiredate); 2881 | ($startdate) = ( split( /=/, $startdate ) )[1]; 2882 | ($expiredate) = ( split( /=/, $expiredate ) )[1]; 2883 | my $isExpired; 2884 | 2885 | if ( $noDateManip == 0 ) { 2886 | my $unix_time = UnixDate( ParseDate($expiredate), "%s" ); 2887 | my $time_now = qx[ date -u ]; 2888 | chomp($time_now); 2889 | my $unix_time_now = UnixDate( ParseDate($time_now), "%s" ); 2890 | $isExpired = GREEN "[VALID]"; 2891 | if ( $unix_time_now > $unix_time ) { 2892 | $isExpired = RED "[EXPIRED]"; 2893 | } 2894 | } 2895 | print YELLOW . $domain . MAGENTA " (" . $cert . ")\n"; 2896 | print WHITE "\t \\_ Not Before: " . GREEN . $startdate . "\n"; 2897 | print WHITE "\t \\_ Not After : " 2898 | . GREEN 2899 | . $expiredate . " " 2900 | . $isExpired . "\n"; 2901 | 2902 | # Check if self-signed 2903 | my $isSelfSigned = 2904 | qx[ openssl verify "$sslcertdir/$cert" | grep 'self signed certificate' ]; 2905 | if ($isSelfSigned) { 2906 | print RED "\t \\_ [WARN] " 2907 | . WHITE "- Self-Signed Certificate\n"; 2908 | } 2909 | else { 2910 | # Get Issuer and display it. 2911 | my $SSLIssuer = 2912 | qx[ openssl x509 -in "$sslcertdir/$cert" -issuer -noout ]; 2913 | my $Oloc = index( $SSLIssuer, "O=" ); 2914 | my $O = substr( $SSLIssuer, $Oloc ); 2915 | my ( $SSLIssuer, $crap ) = ( split( /\//, $O ) )[0]; 2916 | $SSLIssuer = substr( $SSLIssuer, 2 ); 2917 | chomp($SSLIssuer); 2918 | print GREEN "\t \\_ [CA SIGNED] " 2919 | . WHITE "Issued by: $SSLIssuer\n"; 2920 | } 2921 | 2922 | # List SAN's 2923 | print WHITE 2924 | "This certificate is protecting the following Subject Alternative Names:\n"; 2925 | my $SAN; 2926 | my @getSANS = 2927 | qx[ openssl x509 -in "$sslcertdir/$cert" -noout -text | grep -oP '(?<=DNS:)[a-z0-9.-]+' ]; 2928 | foreach $SAN (@getSANS) { 2929 | chomp($SAN); 2930 | print YELLOW "\t \\_ " . CYAN $SAN . "\n"; 2931 | } 2932 | } 2933 | } 2934 | else { 2935 | print WHITE "No SSL Certificates found for " 2936 | . CYAN $MAINDOMAIN 2937 | . WHITE " (" 2938 | . GREEN $username 2939 | . WHITE ")\n"; 2940 | } 2941 | 2942 | # Check for pending AutoSSL orders here (uses whmapi1 get_autossl_pending_queue (62.0.26+ only) 2943 | if ( $CPVersion gt "11.60.0.26" ) { 2944 | if ( $SSLProvider eq "cPanel (Powered by Comodo)" ) { 2945 | print "\nChecking for pending AutoSSL orders: \n"; 2946 | my $SSL_PENDING = 2947 | qx[ whmapi1 get_autossl_pending_queue | grep -B3 'user: $username' ]; 2948 | if ($SSL_PENDING) { 2949 | my $NEW_SSL_PENDING; 2950 | ( $NEW_SSL_PENDING = $SSL_PENDING ) =~ 2951 | s/order_item_id: /order_item_id: https:\/\/manage2.cpanel.net\/certificate.cgi\?oii=/g; 2952 | $SSL_PENDING = $NEW_SSL_PENDING; 2953 | print GREEN "\t \\_ \n"; 2954 | print GREEN "$SSL_PENDING"; 2955 | } 2956 | else { 2957 | print GREEN "\t \\_ None"; 2958 | } 2959 | print "\n"; 2960 | } 2961 | } 2962 | 2963 | # Check for CAA records here. 2964 | print "Checking for CAA records: \n"; 2965 | my @HasCAA = qx[ dig $MAINDOMAIN caa +short ]; 2966 | my $CAARecord; 2967 | if (@HasCAA) { 2968 | print YELLOW "[NOTE] * CAA records were found for $MAINDOMAIN\n"; 2969 | print GREEN 2970 | "SSL Certificates can only be issued from the following CA's:\n"; 2971 | foreach $CAARecord (@HasCAA) { 2972 | chomp($CAARecord); 2973 | print CYAN "\t \\_ $CAARecord\n"; 2974 | } 2975 | } 2976 | else { 2977 | print GREEN "\t \\_ None\n"; 2978 | } 2979 | &border; 2980 | } 2981 | 2982 | sub isUserReserved { 2983 | my $lcUser = $_[0]; 2984 | my $UserList; 2985 | my @UserList = 2986 | qx[ /usr/local/cpanel/3rdparty/bin/perl -MCpanel::Validate::Username -MData::Dumper -e 'print Dumper(Cpanel::Validate::Username::list_reserved_usernames());' ]; 2987 | foreach $UserList (@UserList) { 2988 | chomp($UserList); 2989 | if ( $UserList =~ $lcUser ) { 2990 | return 1; 2991 | } 2992 | } 2993 | } 2994 | --------------------------------------------------------------------------------