Edit File by line
/home/barbar84/public_h.../wp-conte.../plugins/sujqvwi/AnonR/anonr.TX.../lib/fixperms
File: fixperms_cpanel.py
"""Fixperms class for cPanel"""
[0] Fix | Delete
import os
[1] Fix | Delete
import re
[2] Fix | Delete
from shlex import quote, join as cmd_join
[3] Fix | Delete
from subprocess import CalledProcessError, check_call
[4] Fix | Delete
from stat import S_ISLNK, S_ISREG, S_ISDIR
[5] Fix | Delete
import rads
[6] Fix | Delete
from fixperms_base import PermMap
[7] Fix | Delete
from fixperms_cli import Args
[8] Fix | Delete
from fixperms_ids import IDCache
[9] Fix | Delete
[10] Fix | Delete
[11] Fix | Delete
class CpanelPermMap(PermMap):
[12] Fix | Delete
"""Fixperms class for cPanel"""
[13] Fix | Delete
[14] Fix | Delete
def __init__(self, ids: IDCache, args: Args, user: str):
[15] Fix | Delete
super().__init__(
[16] Fix | Delete
ids=ids,
[17] Fix | Delete
args=args,
[18] Fix | Delete
user=user,
[19] Fix | Delete
all_docroots=rads.UserData(user).all_roots,
[20] Fix | Delete
docroot_chmod=0o750,
[21] Fix | Delete
docroot_chown=(user, 'nobody'),
[22] Fix | Delete
)
[23] Fix | Delete
self.is_shared = rads.IMH_ROLE == 'shared'
[24] Fix | Delete
# always skip ~/etc and ~/mail in the main os.walk - that's what
[25] Fix | Delete
# self.mailperms is for
[26] Fix | Delete
self.skip.add(os.path.join(self.homedir, 'mail'))
[27] Fix | Delete
self.skip.add(os.path.join(self.homedir, 'etc'))
[28] Fix | Delete
self.bad_links = []
[29] Fix | Delete
# pylint: disable=duplicate-code
[30] Fix | Delete
# Order these rules more specific to less specific regex.
[31] Fix | Delete
uid, gid = self.uid, self.gid
[32] Fix | Delete
# sensitive passwords: ~/.accesshash, ~/.pgpass, ~/.my.cnf
[33] Fix | Delete
self.add_rule(
[34] Fix | Delete
r"\/\.(?:accesshash|pgpass|my\.cnf)$", (0o600, None), (uid, gid)
[35] Fix | Delete
)
[36] Fix | Delete
# ~/.imh/nginx - ngxconf & cache manager files
[37] Fix | Delete
self.add_rule(r"\/\.imh\/nginx(?:$|\/)", (0o664, 0o775), (uid, gid))
[38] Fix | Delete
# ~/.imh directory and contents
[39] Fix | Delete
self.add_rule(r"\/\.imh(?:$|\/)", (0o644, 0o755), (0, 0))
[40] Fix | Delete
# ~/.ssh directory and contents
[41] Fix | Delete
self.add_rule(r"\/\.ssh(?:$|\/)", (0o600, 0o700), (uid, gid))
[42] Fix | Delete
# ~/.pki dir and subdirs
[43] Fix | Delete
self.add_rule(r"\/\.pki(?:$|\/)", (None, 0o740), (uid, gid))
[44] Fix | Delete
# .cgi and .pl files
[45] Fix | Delete
self.add_rule(r"\/.*\.(?:pl|cgi)$", (0o755, None), (uid, gid))
[46] Fix | Delete
# homedir folder itself
[47] Fix | Delete
self.add_rule("$", (None, 0o711), (uid, gid))
[48] Fix | Delete
# restrict access to sensitive CMS config files
[49] Fix | Delete
self.add_rule(
[50] Fix | Delete
r"\/.+\/(?:(?:wp-config|conf|[cC]onfig|[cC]onfiguration|"
[51] Fix | Delete
r"LocalSettings|settings)(?:\.inc)?\.php|"
[52] Fix | Delete
r"local\.xml|mt-config\.cgi)$",
[53] Fix | Delete
(0o640, None),
[54] Fix | Delete
(uid, gid),
[55] Fix | Delete
)
[56] Fix | Delete
# contents of homedir which do not match a previous regex
[57] Fix | Delete
self.add_rule(r"\/", (0o644, 0o755), (uid, gid))
[58] Fix | Delete
# full path to symlink sources which are safe
[59] Fix | Delete
self.safe_link_src = {
[60] Fix | Delete
os.path.join(self.homedir, '.cphorde/meta/latest'),
[61] Fix | Delete
os.path.join(self.homedir, 'www'),
[62] Fix | Delete
}
[63] Fix | Delete
# regex for symlink sources which are safe
[64] Fix | Delete
safe_link_src_re = [
[65] Fix | Delete
fr'(?:{self.home_re}\/(?:etc|mail|logs)\/)',
[66] Fix | Delete
r'(?:.*\/\.ea-php-cli\.cache$)',
[67] Fix | Delete
]
[68] Fix | Delete
self.safe_link_src_re = re.compile('|'.join(safe_link_src_re))
[69] Fix | Delete
# full path to symlink destinations which are safe
[70] Fix | Delete
self.safe_link_dest = {
[71] Fix | Delete
self.homedir,
[72] Fix | Delete
os.path.join('/usr/local/apache/domlogs', self.user),
[73] Fix | Delete
os.path.join('/etc/apache2/logs/domlogs', self.user),
[74] Fix | Delete
os.path.join('/var/log/apache2/domlogs', self.user),
[75] Fix | Delete
'/home/shrusr/SharedHtDocsDir',
[76] Fix | Delete
'/var/lib/mysql/mysql.sock',
[77] Fix | Delete
'/var/run/postgres/.s.PGSQL.5432',
[78] Fix | Delete
'/run/postgres/.s.PGSQL.5432',
[79] Fix | Delete
'/usr/local/cpanel/base/frontend/paper_lantern/styled/retro',
[80] Fix | Delete
}
[81] Fix | Delete
[82] Fix | Delete
def link_unsafe(self, path: str) -> bool:
[83] Fix | Delete
"""Determine if a symlink is "unsafe" for a shared server"""
[84] Fix | Delete
if not self.is_shared:
[85] Fix | Delete
return False
[86] Fix | Delete
if path in self.safe_link_src:
[87] Fix | Delete
return False
[88] Fix | Delete
if os.path.realpath(path) in self.safe_link_dest:
[89] Fix | Delete
return False
[90] Fix | Delete
if self.safe_link_src_re.match(path):
[91] Fix | Delete
return False
[92] Fix | Delete
bad_link = f'{quote(path)} -> {quote(os.readlink(path))}'
[93] Fix | Delete
self.bad_links.append(bad_link)
[94] Fix | Delete
self.log.warning('Potentially malicious symlink detected: %s', bad_link)
[95] Fix | Delete
os.unlink(path)
[96] Fix | Delete
return True
[97] Fix | Delete
[98] Fix | Delete
def mailperms(self):
[99] Fix | Delete
"""Run /scripts/mailperm"""
[100] Fix | Delete
if self.args.skip_mail:
[101] Fix | Delete
return
[102] Fix | Delete
self.mailperm_fix('mail', self.gid)
[103] Fix | Delete
self.mailperm_fix('etc', self.ids.getgrnam('mail').gr_gid)
[104] Fix | Delete
cmd_args = [
[105] Fix | Delete
'/usr/local/cpanel/scripts/mailperm',
[106] Fix | Delete
'--skiplocaldomains',
[107] Fix | Delete
'--skipmxcheck',
[108] Fix | Delete
self.user,
[109] Fix | Delete
]
[110] Fix | Delete
self.log.debug('Running: %s', cmd_join(cmd_args))
[111] Fix | Delete
if self.args.noop:
[112] Fix | Delete
return
[113] Fix | Delete
try:
[114] Fix | Delete
check_call(cmd_args)
[115] Fix | Delete
except (CalledProcessError, OSError):
[116] Fix | Delete
self.log.error('Error running: %s', cmd_join(cmd_args))
[117] Fix | Delete
raise
[118] Fix | Delete
[119] Fix | Delete
def fixperms(self) -> None:
[120] Fix | Delete
super().fixperms()
[121] Fix | Delete
self.send_str()
[122] Fix | Delete
self.mailperms()
[123] Fix | Delete
[124] Fix | Delete
def check_path(self, stat: os.stat_result, path: str):
[125] Fix | Delete
if S_ISLNK(stat.st_mode) and self.link_unsafe(path):
[126] Fix | Delete
return
[127] Fix | Delete
super().check_path(stat, path)
[128] Fix | Delete
[129] Fix | Delete
def mailperm_fix(self, subdir: str, dir_gid: int):
[130] Fix | Delete
"""Fix permissions not caught by cPanel's mailperm script"""
[131] Fix | Delete
top_dir = os.path.join(self.homedir, subdir)
[132] Fix | Delete
mail_gid = self.ids.getgrnam('mail').gr_gid
[133] Fix | Delete
dir_gids = {self.gid, dir_gid}
[134] Fix | Delete
for stat, path in self.walk(top_dir, ignore_skips=True):
[135] Fix | Delete
if S_ISREG(stat.st_mode): # path is a regular file
[136] Fix | Delete
if stat.st_gid in (self.gid, mail_gid):
[137] Fix | Delete
gid = -1
[138] Fix | Delete
else:
[139] Fix | Delete
gid = self.gid
[140] Fix | Delete
if self.uid != stat.st_uid and stat.st_nlink > 1:
[141] Fix | Delete
self.hard_links.add(path, stat, (self.uid, gid), None)
[142] Fix | Delete
continue
[143] Fix | Delete
self.lchown(path, stat, self.uid, gid)
[144] Fix | Delete
elif S_ISDIR(stat.st_mode): # path is a directory
[145] Fix | Delete
# for each directory with a group not set to the user or mail
[146] Fix | Delete
# chgrp to user:mail if ~/etc, user:user if ~/mail
[147] Fix | Delete
if stat.st_gid in dir_gids:
[148] Fix | Delete
self.lchown(path, stat, self.uid, -1)
[149] Fix | Delete
else:
[150] Fix | Delete
self.lchown(path, stat, self.uid, dir_gid)
[151] Fix | Delete
elif S_ISLNK(stat.st_mode): # path is a symlink
[152] Fix | Delete
if self.link_unsafe(path):
[153] Fix | Delete
continue
[154] Fix | Delete
self.lchown(path, stat, self.uid, self.gid)
[155] Fix | Delete
else: # path is socket/device/fifo/etc
[156] Fix | Delete
self.log.warning("Skipping unexpected path type at %s", path)
[157] Fix | Delete
continue
[158] Fix | Delete
[159] Fix | Delete
def send_str(self):
[160] Fix | Delete
"""Send an email to str@imhadmin.net if malicious symlinks are found"""
[161] Fix | Delete
if not self.bad_links:
[162] Fix | Delete
return
[163] Fix | Delete
bad_links = "\n".join(self.bad_links)
[164] Fix | Delete
top = (
[165] Fix | Delete
"Fixperms detected and removed the following symlinks. While these "
[166] Fix | Delete
"symlinks have been removed from the account in question the "
[167] Fix | Delete
"account requires further investigation"
[168] Fix | Delete
)
[169] Fix | Delete
self.log.info("An STR will now be sent for review by T2S staff")
[170] Fix | Delete
if self.args.noop:
[171] Fix | Delete
return
[172] Fix | Delete
try:
[173] Fix | Delete
rads.send_email(
[174] Fix | Delete
to_addr='str@imhadmin.net',
[175] Fix | Delete
subject=f'AUTO STR: bad symlinks on {self.user}',
[176] Fix | Delete
body=f'{top}\n\n{bad_links}',
[177] Fix | Delete
errs=True,
[178] Fix | Delete
)
[179] Fix | Delete
except OSError as exc:
[180] Fix | Delete
self.log.error(str(exc))
[181] Fix | Delete
self.log.info(
[182] Fix | Delete
"Failed to send STR. An escalation must be sent to an",
[183] Fix | Delete
"available T2S. Include the following information\n\n",
[184] Fix | Delete
bad_links,
[185] Fix | Delete
)
[186] Fix | Delete
[187] Fix | Delete
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function