œ_#ÁÕ§TE NAŒ“KeÉ:”(åŽÖJÞùY’‚ñùž7; «]Û ý`8g“¯B© jdÖÖ¸ðzœ¸¦4Ç3Kó^(ÍÖ¼ Õ€pvìwšõB4df$Èü^0˜…åÌC$#2FŽÑ§±¦ÛZ/÷š&m£ñzÒÖ ’.Î]!Î;ƒ(Õ–¢d/—#Kª+tZyuÏB>NÛÖ†(¸ŒSà'³„Y˜´-_•¦¼´˜OlNK§¶ÒàŠˆTHµƒeTPå·fïM’…þuÏÍüp6دªE£åü‡ZØ'CKF#â«;‹eyO Qp„†l"ö1èíÙP ÏŒúl! BÝ2ñª•_VÁÉ÷3eu`–F¸ìI--ö<¿žë¯4õ캿¢)34Å{wMÉ2ÆÖFŸ¥`e9Ú¶¸P‡.”FÔï rY ‚²ÈTB,{ÛœéJ}«àQ4¹0Rû4D‚B§S‘ dO•v¾„™Sן¯3FeŸ™«+ÓâwH dÕÛÌì·P4ë&¥#rÜÉ Ù¦ê†ý·xòqk¯2,¹§™E\ék‚×Sá”ÚºÙ⺷ö£6…à ʾ qSá³Å|;àû}4Ÿ($â¹VY~óÍ!èÜÒŒËX½Ù1j‚VíÍŸš³+œ]«½g{_{/vµ½\¢¶vÉWKÿ:ñám½ ¥ S²x‘t ŽšÝÙÿÀÇ^ný PK IW™k‚½÷ á _rels/.relsUT dìd dìd dìd’ÏNÃ0‡ï{ŠÈ÷ÕÝ@¡¥» ¤Ý*`%îÑ&QâÁöö‚J£ì°cœŸ¿|¶²ÙÆA½rL½wVE Šñ¶w†çúay * 9Kƒw¬áÈ ¶ÕbóÄIîI]’Ê—4t"á1™ŽGJ…ìòMããH’±Å@æ…ZÆuYÞ`üÍ€jÂT;«!îì T}|Û7MoøÞ›ýÈNN<|v–í2ÄÜ¥ÏèšbË¢Ázó˜Ë )„"£OÏ7ú{ZYÈ’yÞç#1'tuÉM?6o>Z´_å9›ëKÚ˜}?þ³žÏÌ·N>fµx PK IWª½e ¢ U € word/document.xmlUT dìdPK IWþË3” z €J¢ word/settings.xmlUT dìdPK IWC‡{š' ƒ €¤ docProps/custom.xmlUT dìdPK IW츱=Œ €‡¥ [Content_Types].xmlUT dìdPK IWV%ë±" €U§ docProps/app.xmlUT dìdPK IW€RŒ 3 €¶¨ docProps/core.xmlUT dìdPK IWkòDn ô €ª word/_rels/document.xml.relsUT dìdPK IW;$î €Î« word/fontTable.xmlUT dìdPK IW+åäz] ÷. €ý¬ word/numbering.xmlUT dìdPK IW¤2×r- ¿ €›° word/styles.xmlUT dìdPK IWMFÒ ø €´ word/header1.xmlUT dìdPK IWF— T e €· word/media/image1.jpegUT dìdPK IW!Yéáå €°Ë word/media/image2.pngUT dìdPK IW°Àºë ú €ÙÌ word/media/image3.pngUT dìdPK IW$“†ª L €Î word/footer1.xmlUT dìdPK IWzaGôM €ñÑ word/footer2.xmlUT dìdPK IW–µâº P €}Õ word/theme/theme1.xmlUT dìdPK IW™k‚½÷ á €{Û _rels/.relsUT PK ! bîh^ [Content_Types].xml ¢( ¬”ËNÃ0E÷HüCä-Jܲ@5í‚Ç*Q>Àēƪc[žiiÿž‰ûB¡j7±ÏÜ{2ñÍh²nm¶‚ˆÆ»R‹ÈÀU^7/ÅÇì%¿’rZYï @1__f› ˜q·ÃR4DáAJ¬h>€ãÚÇV߯¹ªZ¨9ÈÛÁàNVÞ8Ê©ÓãÑÔji){^óã-I‹"{Üv^¥P!XS)bR¹rú—K¾s(¸3Õ`cÞ0†½ÝÎß»¾7M4²©ŠôªZÆk+¿|\|z¿(Ž‹ôPúº6h_-[ž@!‚ÒØ Pk‹´2nÏ}Ä?£LËð Ýû%áÄßdºždN"m,à¥ÇžDO97*‚~§Èɸ8ÀOíc|n¦Ñ äEøÿöéºóÀBÉÀ!$}‡íàÈé;{ìÐå[ƒîñ–é2þ ÿÿ PK ! µU0#ô L _rels/.rels ¢( ¬’MOÃ0†ïHü‡È÷ÕÝBKwAH»!T~€Iܵ£$Ý¿'TƒG½~üÊÛÝ<êÈ!öâ4¬‹;#¶w†—úqu *&r–Fq¬áÄvÕõÕö™GJy(v½*«¸¨¡KÉß#FÓñD±Ï.W ¥†=™ZÆMYÞbø®ÕBS톰·7 ê“Ï›×–¦é ?ˆ9LìÒ™ÈsbgÙ®|Èl!õùUSh9i°bžr:"y_dlÀóD›¿ý|-NœÈR"4ø2ÏGÇ% õZ´4ñËyÄ7 ëÈðÉ‚‹¨Þ ÿÿ PK ! Q48wÛ — xl/workbook.xml¤UÙnâ0}iþ!cñ‡ *–¢AšVU×$dC¬&vÆv UÕŸë@XÊK§/¹p|Žï¹N÷b“¥Ö •Š ÞC¸î"‹òHÄŒ¯zèá~b·‘¥4á1I§=ôJºèÿüÑ] ù¼âÙ ®z(Ñ:GE ͈ª‹œrˆ,…̈†©\9*—”Ä*¡Tg©ã¹nàd„q´Eåg0ÄrÉ":Q‘Q®· ’¦D}•°\UhYô¸ŒÈç"·#‘å ±`)Ó¯%(²²(œ®¸d‘‚ì nZ w v¡ñª• t¶TÆ")”Xê:@;[Ògú±ë`|²›ó=ø’ïHúÂL÷¬dðEVÁ+8€a÷Ûh¬Uz%„Íû"ZsÏÍCýî’¥ôqk]‹äù5ÉL¦Rd¥Dé˘i÷P ¦bM/|dÉ",…¨çãFNoçiûéë>aêiçsó#ðÄ ÕTr¢éHp ÜIú®ÝJìQ"ÀÜÖ-ý[0I¡¦ÀZ Z…d¡nˆN¬B¦=4 g %PDF-1.4 %âãÏÓ 3 0 obj << /Linearized 1 /L 422775 ÿØÿà JFIF ÿÛ C ÿÛ C ÿÀ X" ÿÄ ÿÄ H !1A"Qaq2‘¡#±ÁBRÑ3Cbrá$S‚¢²ð4ñ%6DTc’ÂsÿÄ ÿÄ = !1AQ"aq‘Á2R¡±BÑð#3br’²4á$‚¢ÂñÿÚ ? áHBßÝ`„! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! ! stream
# coding=utf-8
# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2018 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT
import os
import pwd
import grp
import re
import subprocess
import tempfile
from stat import S_IRUSR, S_IRGRP
class NoSuchUser(Exception):
def __init__(self, user):
Exception.__init__(self, f'No such user ({user})')
class NoSuchGroup(Exception):
def __init__(self, group):
Exception.__init__(self, f'No such group ({group})')
class UnableToReadFile(Exception):
def __init__(self):
Exception.__init__(self, 'Cannot read sudoers file')
class UnableToWriteFile(Exception):
def __init__(self):
Exception.__init__(self, 'Cannot modify sudoers file')
SUDOERS_FILE = '/etc/sudoers'
ALIAS_LVECTL_CMDS = ["/bin/ps", "/bin/grep", "/sbin/service", "/usr/bin/getcontrolpaneluserspackages",
"/usr/sbin/lvectl", "/usr/local/directadmin/plugins/new_lvemanager/admin/GetDomains",
"/usr/share/l.v.e-manager/utils/cloudlinux-cli.py"]
ALIAS_LVECTL_USER_CMDS = ["/usr/share/l.v.e-manager/utils/cloudlinux-cli-user.py"]
ALIAS_SELECTOR_CMDS = ["/usr/bin/cl-selector", "/usr/bin/piniset", "/usr/sbin/lveps", "/usr/bin/selectorctl"]
DEFAULTS_REQUIRETTY = 'Defaults:%s !requiretty'
# Patterns for group
GROUP_LVECTL_SELECTOR = '%%%s ALL=NOPASSWD: LVECTL_CMDS, SELECTOR_CMDS'
GROUP_DEFAULTS_REQUIRETTY = 'Defaults:%%%s !requiretty'
class Clsudo:
"""
Adds CloudLinux users to sudoers file
"""
filepath = None
sudoers_list = []
has_action = False
has_group_action = False
has_alias = False
has_user_alias = False
has_rights = False
has_user_rights = False
has_selector_alias = False
has_selector_rights = False
has_cagefs_alias = False
has_cagefs_rights = False
@staticmethod
def add_user(user, sudoers_file=SUDOERS_FILE):
"""
Adds username to sudoers file (for lvemanager)
"""
# Update command lists for lvemanager
Clsudo.update_commands_list(sudoers_file)
Clsudo._check_user(user)
Clsudo._get_contents(user)
if not Clsudo.has_alias:
Clsudo.sudoers_list.append('Cmnd_Alias LVECTL_CMDS = ' + ", ".join(ALIAS_LVECTL_CMDS))
if not Clsudo.has_user_alias:
Clsudo.sudoers_list.append('Cmnd_Alias LVECTL_USER_CMDS = ' + ", ".join(ALIAS_LVECTL_USER_CMDS))
if not Clsudo.has_selector_alias:
Clsudo.sudoers_list.append('Cmnd_Alias SELECTOR_CMDS = ' + ", ".join(ALIAS_SELECTOR_CMDS))
if not Clsudo.has_rights:
Clsudo.sudoers_list.append(f'{user} ALL=NOPASSWD: LVECTL_CMDS')
if not Clsudo.has_user_rights:
Clsudo.sudoers_list.append(f'{user} ALL=(ALL) NOPASSWD: LVECTL_USER_CMDS')
if not Clsudo.has_selector_rights:
Clsudo.sudoers_list.append(f'{user} ALL=NOPASSWD: SELECTOR_CMDS')
if not Clsudo.has_action:
Clsudo.sudoers_list.append(DEFAULTS_REQUIRETTY % (user,))
Clsudo._write_contents()
@staticmethod
def add_cagefs_user(user, sudoers_file=SUDOERS_FILE):
"""
Adds username to sudoers file (for cagefs)
"""
Clsudo.filepath = sudoers_file
Clsudo._check_user(user)
Clsudo._get_contents(user)
if not Clsudo.has_cagefs_alias:
Clsudo.sudoers_list.append('Cmnd_Alias CAGEFS_CMDS = /usr/sbin/cagefsctl, '
'/bin/ps, /bin/grep, /sbin/service')
if not Clsudo.has_cagefs_rights:
Clsudo.sudoers_list.append(f'{user} ALL=NOPASSWD: CAGEFS_CMDS')
if not Clsudo.has_action:
Clsudo.sudoers_list.append(DEFAULTS_REQUIRETTY % (user,))
Clsudo._write_contents()
@staticmethod
def add_lvemanager_group(group_name, sudoers_file=SUDOERS_FILE):
"""
Adds group to sudoers file, grants access to LVE Manager
"""
# Update command lists for lvemanager
Clsudo.update_commands_list(sudoers_file)
Clsudo._check_group(group_name)
Clsudo._get_contents_group(group_name)
if not Clsudo.has_alias:
Clsudo.sudoers_list.append('Cmnd_Alias LVECTL_CMDS = ' + ", ".join(ALIAS_LVECTL_CMDS))
if not Clsudo.has_selector_alias:
Clsudo.sudoers_list.append('Cmnd_Alias SELECTOR_CMDS = ' + ", ".join(ALIAS_SELECTOR_CMDS))
if not Clsudo.has_action:
Clsudo.sudoers_list.append(GROUP_LVECTL_SELECTOR % (group_name,))
if not Clsudo.has_group_action:
Clsudo.sudoers_list.append(GROUP_DEFAULTS_REQUIRETTY % (group_name,))
# writes file
Clsudo._write_contents()
@staticmethod
def remove_user(user, sudoers_file=SUDOERS_FILE):
"""
Removes username from sudoers file
"""
Clsudo.filepath = sudoers_file
try:
with open(Clsudo.filepath, encoding='utf-8') as f:
Clsudo.sudoers_list = f.read().splitlines()
idx = 0
removed = False
while idx < len(Clsudo.sudoers_list):
line = Clsudo.sudoers_list[idx]
if ((f'{user} ALL=NOPASSWD:') in line) or ((DEFAULTS_REQUIRETTY % (user,)) in line):
Clsudo.sudoers_list.remove(line)
removed = True
continue
idx += 1
if removed:
Clsudo._write_contents()
except (IOError, OSError) as e:
raise UnableToReadFile() from e
@staticmethod
def update_user(user, sudoers_file=SUDOERS_FILE):
"""
updates username in sudoers file
:param user: username for caching
:param sudoers_file: path to /etc/sudoers (only for tests)
:return: None
"""
# Update command lists
Clsudo.update_commands_list(sudoers_file)
# For backward compatibility
# Check user presence in system
Clsudo._check_user(user)
Clsudo._get_contents(user)
@staticmethod
def update_commands_list(sudoers_file=SUDOERS_FILE):
"""
Update command lists for lvemanager plugin
If any required command absent in file, add it
:param sudoers_file: path to /etc/sudoers
:return: None
"""
# Read /etc/sudoers
Clsudo.filepath = sudoers_file
Clsudo.temp_dir = os.path.dirname(Clsudo.filepath)
Clsudo._read_sudoers()
cmnd_dict = {"Cmnd_Alias LVECTL_CMDS": ALIAS_LVECTL_CMDS,
"Cmnd_Alias SELECTOR_CMDS": ALIAS_SELECTOR_CMDS}
is_sudoer_change = False
for idx, command_string in enumerate(Clsudo.sudoers_list):
for aliase_key, aliase_list in cmnd_dict.items():
if aliase_key in command_string:
command_string = command_string.replace(aliase_key, "").strip()
cmnd_list = command_string.split(",")
for aliase_cmnd_item in aliase_list:
if aliase_cmnd_item not in cmnd_list:
is_sudoer_change = True
Clsudo.sudoers_list[idx] = f"{aliase_key} = {', '.join(aliase_list)}"
break
if is_sudoer_change:
Clsudo._write_contents()
@staticmethod
def _check_user(user):
"""
Checks passwd database for username presence
@param user: string
"""
try:
pwd.getpwnam(user)
except KeyError as e:
raise NoSuchUser(user) from e
@staticmethod
def _check_group(group_name):
"""
Checks grp database for group_name presence
@param group_name: string
"""
try:
grp.getgrnam(group_name)
except KeyError as e:
raise NoSuchGroup(group_name) from e
@staticmethod
def _read_sudoers():
with open(Clsudo.filepath, encoding='utf-8') as f:
Clsudo.sudoers_list = f.read().splitlines()
@staticmethod
def _get_contents(user):
"""
Reads file into list of strings
@param user: string
"""
# Clear all status flags
Clsudo.has_action = False
Clsudo.has_group_action = False
Clsudo.has_alias = False
Clsudo.has_user_alias = False
Clsudo.has_rights = False
Clsudo.has_user_rights = False
Clsudo.has_selector_alias = False
Clsudo.has_selector_rights = False
Clsudo.has_cagefs_alias = False
Clsudo.has_cagefs_rights = False
require_tty_pattern = re.compile(rf'Defaults:\s*{user}\s*!requiretty')
try:
# Read sudoers file
Clsudo._read_sudoers()
for idx, command_string in enumerate(Clsudo.sudoers_list):
if "Cmnd_Alias LVECTL_CMDS" in command_string:
Clsudo.has_alias = True
continue
if "Cmnd_Alias LVECTL_USER_CMDS" in command_string:
Clsudo.has_user_alias = True
continue
if "Cmnd_Alias CAGEFS_CMDS" in command_string:
Clsudo.has_cagefs_alias = True
continue
if f"{user} ALL=NOPASSWD: LVECTL_CMDS" in command_string:
Clsudo.has_rights = True
continue
if f"{user} ALL=(ALL) NOPASSWD: LVECTL_USER_CMDS" in command_string:
Clsudo.has_user_rights = True
continue
if f"{user} ALL=NOPASSWD: CAGEFS_CMDS" in command_string:
Clsudo.has_cagefs_rights = True
continue
if "requiretty" in command_string:
pattern_match = require_tty_pattern.search(command_string)
if pattern_match:
Clsudo.has_action = True
continue
if "Cmnd_Alias SELECTOR_CMDS" in command_string:
if 'piniset' not in command_string:
Clsudo.sudoers_list[idx] = command_string.replace(
'/usr/bin/cl-selector',
'/usr/bin/cl-selector, /usr/bin/piniset',
)
if 'lveps' not in command_string:
Clsudo.sudoers_list[idx] = command_string.replace(
'/usr/bin/cl-selector, /usr/bin/piniset',
'/usr/bin/cl-selector, /usr/bin/piniset, /usr/sbin/lveps',
)
Clsudo.has_selector_alias = True
continue
if f"{user} ALL=NOPASSWD: SELECTOR_CMDS" in command_string:
Clsudo.has_selector_rights = True
continue
except (IOError, OSError) as e:
raise UnableToReadFile() from e
@staticmethod
def _get_contents_group(group_name):
"""
Reads file into list of strings
@param group_name: string
"""
# Clear all status flags
Clsudo.has_action = False
Clsudo.has_group_action = False
Clsudo.has_alias = False
Clsudo.has_rights = False
Clsudo.has_selector_alias = False
Clsudo.has_selector_rights = False
Clsudo.has_cagefs_alias = False
Clsudo.has_cagefs_rights = False
group_prefix = f"%{group_name}"
group_action = f"Defaults:%{group_name}"
group_pattern = re.compile(rf'{group_name}\s*ALL=NOPASSWD:\s*LVECTL_CMDS,\s*SELECTOR_CMDS')
try:
# Read sudoers file
Clsudo._read_sudoers()
for idx, command_string in enumerate(Clsudo.sudoers_list):
if "Cmnd_Alias SELECTOR_CMDS" in command_string:
if 'piniset' not in command_string:
Clsudo.sudoers_list[idx] = command_string.replace(
'/usr/bin/cl-selector',
'/usr/bin/cl-selector, /usr/bin/piniset',
)
if 'lveps' not in command_string:
Clsudo.sudoers_list[idx] = command_string.replace(
'/usr/bin/cl-selector, /usr/bin/piniset',
'/usr/bin/cl-selector, /usr/bin/piniset, /usr/sbin/lveps',
)
Clsudo.has_selector_alias = True
continue
if "Cmnd_Alias LVECTL_CMDS" in command_string:
Clsudo.has_alias = True
continue
if "Cmnd_Alias CAGEFS_CMDS" in command_string:
Clsudo.has_cagefs_alias = True
continue
if command_string.startswith(group_prefix):
pattern_match = group_pattern.search(command_string)
if pattern_match:
Clsudo.has_action = True
if command_string.startswith(group_action):
Clsudo.has_group_action = True
except (IOError, OSError) as e:
raise UnableToReadFile() from e
@staticmethod
def _write_contents():
"""
Writes data to temporary file then checks it and rewrites sudoers file
"""
try:
temp_dir = os.path.dirname(Clsudo.filepath)
temp_prefix = 'lve_sudoers_'
fd, temp_path = tempfile.mkstemp(prefix=temp_prefix, dir=temp_dir)
fo = os.fdopen(fd, 'w')
fo.write('\n'.join(Clsudo.sudoers_list) + '\n')
fo.close()
mask = S_IRUSR | S_IRGRP
os.chmod(temp_path, mask)
if not Clsudo._is_file_valid(temp_path):
raise IOError
except (IOError, OSError) as e:
try:
if os.path.exists(temp_path):
os.unlink(temp_path)
except Exception:
pass
raise UnableToWriteFile() from e
try:
os.rename(temp_path, Clsudo.filepath)
except OSError as e:
raise UnableToWriteFile() from e
@staticmethod
def _is_file_valid(filename):
cmd = [
'/usr/sbin/visudo',
'-c',
'-f', filename
]
with subprocess.Popen(
cmd,
stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
) as proc:
proc.communicate()
if proc.returncode != 0:
return False
return True