????
Current Path : /usr/local/ssl/share/cagefs/ |
Current File : //usr/local/ssl/share/cagefs/repair_homes.py |
#!/opt/cloudlinux/venv/bin/python3 -bb # -*- coding: utf-8 -*- # Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2019 All Rights Reserved # # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENSE.TXT from __future__ import print_function from __future__ import absolute_import from __future__ import division from __future__ import unicode_literals from future import standard_library standard_library.install_aliases() from builtins import * import getopt import os import pwd import shutil import subprocess import sys import time from clcommon.utils import mod_makedirs from clcagefslib.const import BASEDIR from secureio import print_error LOGFILE = '/usr/share/cagefs/repair_homes.log' BASE_HOME_DIR = '/home' PASSWD = '/etc/passwd' HTTPD_CONF = '/usr/local/apache/conf/httpd.conf' USERDATA_UPDATE = '/usr/local/cpanel/bin/userdata_update' USERMOD = '/usr/sbin/usermod' USERDATA = '/var/cpanel/userdata' # Function to uninstall cagefs.etc CAGEFS_ETC='/etc/cagefs.etc' DEBUG_PREFIX='' def uninstall_cagefs_etc(): try: dirList = os.listdir(DEBUG_PREFIX + CAGEFS_ETC) except OSError: return for _file in dirList: target = DEBUG_PREFIX + '/etc/' + _file origin = DEBUG_PREFIX + CAGEFS_ETC + '/' + _file if not os.path.islink(target): continue try: os.unlink(target) except OSError: print('Warning: failed to remove', target) try: os.rename(origin, target) except OSError: print('Warning: failed to move', origin, 'to', target) # Functions for unmounting users for CageFS 2.0 UMOUNT='/bin/umount' def umount_list(_list): _list.sort() _list.reverse() for line in _list: subprocess.call([UMOUNT, "-l", line]) def get_mounted_dirs(): mounts = open("/proc/mounts", "r").readlines() _list = [] for line in mounts: mountpoint = line.split()[1] if mountpoint.find(BASEDIR) != -1: _list.append(mountpoint[mountpoint.find('/'):]) return _list def umount(user): subdir = BASEDIR + '/'+user[-2:]+ '/'+user+'/' mounts = open("/proc/mounts", "r").readlines() mylist = [] for line in mounts: mountpoint = line.split()[1] if mountpoint.find(subdir) != -1: mylist.append(mountpoint[mountpoint.find('/'):]) umount_list(mylist) # Returns True if unmounting is done def umount_all(): dirs = get_mounted_dirs() if len(dirs) != 0: umount_list(dirs) return True return False # Functions for enabling/disabling users for CageFS 2.0 INIPREFIX='/etc/cagefs/' disabled_dir = INIPREFIX+'users.disabled' enabled_dir = INIPREFIX+'users.enabled' def toggle_file(_dir, username, enable): prefix = username[-2:] fname = '/'+prefix+'/'+username if enable: try: os.remove(_dir + fname) except OSError: pass try: os.rmdir(_dir + '/'+prefix) except OSError: pass else: try: mod_makedirs(_dir+'/'+prefix, 0o751) except OSError: pass try: open(_dir + fname, 'w').close() os.chmod(_dir + fname, 0o644) except OSError: pass def toggle_user(username, enable): if os.path.isdir(disabled_dir): toggle_file(disabled_dir, username, enable) if os.path.isdir(enabled_dir): toggle_file(enabled_dir, username, not enable) def disable_user(user): toggle_user(user, False) umount(user) def enable_user(user): toggle_user(user, True) def is_text_file(path): if os.path.isfile(path): p = subprocess.Popen(['file','-bi', path], stdout=subprocess.PIPE, text=True) out, _ = p.communicate() if 'text' in out: return True return False def confirm(message): print(message, end=' ', flush=True) while True: line = sys.stdin.readline() if line == "yes\n": break elif line == "no\n": print("Aborting") sys.exit(0) print("Please, reply with yes or no") # Returns True if users with invalid pathes to home directories exist def invalid_homes_exist(): # get all users from /etc/passwd pw = pwd.getpwall() for line in pw: if line.pw_dir.startswith('/var/cagefs/'): return True return False def print_log(log, *messages): for msg in messages: print(msg, end=' ') print(msg, end=' ', file=log) print("") print("", file=log) # Function sets home directory for specified user # Returns True if error has occured def usermod(user, home_dir, log): try: ret = subprocess.call([USERMOD, "-d", home_dir, user]) if ret != 0: print_log(log, "Error:", USERMOD, "-d", home_dir, user, "failed") return True except OSError: print_log(log, 'Error: failed to run', USERMOD, "-d", home_dir, user) return True return False # Run a subprocess with list of options. Log output of subprocess. # Return True if error has occured def run_subprocess(log, command_line_list): error = False try: # run the command and suppress it's output p = subprocess.Popen(command_line_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) (stdoutdata, stderrdata) = p.communicate() if stdoutdata != None: print_log(log, stdoutdata) if stderrdata != None: print_log(log, stderrdata) # check return code of the child if p.returncode != 0: error = True except OSError: print_log(log, 'Error: failed to run', command_line_list) error = True return error def repair_homes(ask = True): log = open(LOGFILE, 'a+') if not invalid_homes_exist(): print_log(log, 'Users with invalid pathes to home directories do NOT exist') log.close() return print("Users that have invalid path to home directory are found") print("(users that have path to home directory starting with /var/cagefs).") print('This script will move home directories to "'+BASE_HOME_DIR+'" and change pathes to home directories') print("in /etc/passwd, /var/cpanel/userdata and /usr/local/apache/conf/httpd.conf") print("Log of all operations will be written to", LOGFILE) print("Backups will be created automatically.") print("") if ask: confirm("Do you want to continue (yes/no)? ") # Print current date and time cur_time = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()) print_log(log, "*** Repair started", cur_time) # Make backups shutil.copyfile(PASSWD, PASSWD+'.repair.bak') print_log(log, "Created backup", PASSWD+'.repair.bak') shutil.copyfile(HTTPD_CONF, HTTPD_CONF+'.repair.bak') print_log(log, "Created backup", HTTPD_CONF+'.repair.bak') # Read httpd.conf print_log(log, 'Reading', HTTPD_CONF, '...') _file = open(HTTPD_CONF, "r") httpd_conf = _file.readlines() _file.close() # get all users from /etc/passwd pw = pwd.getpwall() for line in pw: if line.pw_dir.startswith('/var/cagefs/'): # pw_dir is like /var/cagefs/[prefix]/[parent-user]/home/[parent-user]/[invalid-user] # or # like /var/cagefs/[prefix]/[parent-user]/var/cagefs/[prefix2]/[parent-user2]/home/[parent-user2]/[parent-user]/[invalid-user] # etc... print_log(log, "Repairing user", line.pw_name, "...") # Get name of invalid user invalid_user = os.path.basename(line.pw_dir) if invalid_user != line.pw_name: print_log(log, 'Error: Cannot repair home path', line.pw_dir, 'for user', line.pw_name) continue # Get /var/cagefs/[prefix]/[parent-user]/home/[parent-user] # or /var/cagefs/[prefix]/[parent-user]/var/cagefs/[prefix2]/[parent-user2]/home/[parent-user2]/[parent-user] var_cagefs_home_of_parent = os.path.dirname(line.pw_dir) # Get name of "parent" of invalid user parent = os.path.basename( var_cagefs_home_of_parent ) # Get passwd info for parent try: pw_line = pwd.getpwnam(parent) except Exception as e: print_log(log, 'Cannot repair home path', line.pw_dir, 'for user', line.pw_name) print_log(log, 'Error: "Parent" user', parent, 'does NOT exist') print_log(log, str(e)) continue # Get home of parent parent_home = pw_line.pw_dir # if not var_cagefs_home_of_parent.endswith(parent_home): # print_log(log, 'Cannot repair home path', line.pw_dir, 'for user', line.pw_name) # print_log(log, 'Error: Path to home directory in /var/cagefs for parent user is invalid') # print_log(log, 'Parent user:', parent) # print_log(log, 'Home directory of parent user:', parent_home) # print_log(log, 'Home directory of parent user in /var/cagefs:', var_cagefs_home_of_parent) # continue # Get home base (parent) directory (commonly "/home") if parent_home.startswith('/var/cagefs/'): # Cyclic error. Parent user should be repaired already. So home of parent should be "/home" base_home = BASE_HOME_DIR else: base_home = os.path.dirname(parent_home) print_log(log, 'Base home directory:', base_home) # Incorrect (invalid) location of home dir of invalid user src = base_home+'/'+parent+'/'+invalid_user # Correct location of home dir of invalid user dest = base_home+'/'+invalid_user # Check that home dir of invalid user exists in home dir of "parent" user # (Check that /home/[parent-user]/[invalid-user] exists) if not os.path.isdir(src): # Check that home dir of invalid user is in proper location already if not os.path.isdir(dest): print_log(log, 'Error: home directory of user', invalid_user, 'is NOT found') print_log(log, 'Searched locations:', src, "and", dest) continue else: # Home dir is NOT moved yet # Disable and unmount user disable_user(invalid_user) # if src is symlink, move dir which is pointed by that symlink src = os.path.realpath(src) # remove dest if it is symlink if os.path.islink(dest): try: os.unlink(dest) except (OSError, IOError): pass # Check that "correct" home directory of invalid_user does NOT exist yet if not os.path.exists(dest): # Do "mv /home/[parent-user]/[invalid-user] /home/[invalid-user]" try: os.rename(src, dest) except (OSError, IOError): print_log(log, 'Error while moving', src, 'to', dest) enable_user(invalid_user) continue # Check that moving directory was successfull if (not os.path.isdir(dest)) or os.path.exists(src): print_log(log, 'Error: moving', src, 'to', dest, 'was NOT successfull') enable_user(invalid_user) continue else: # Destination (correct) home directory already exists print_log(log, 'Warning: home directory', dest, 'of user', invalid_user, 'already exists') print_log(log, 'Warning: home directory of user', invalid_user, 'is NOT moved') # Home dir is moved already # Change path to home dir in /etc/passwd if usermod(invalid_user, dest, log): enable_user(invalid_user) continue # Change path to home dir in httpd.conf for ind in range(len(httpd_conf)): if httpd_conf[ind].find('/var/cagefs/') != -1: temp = httpd_conf[ind].replace(line.pw_dir, dest) # Invalid path is repaired correctly ? # line.pw_dir is NOT a part of another more long invalid path (as a result of cyclic error) ? if temp.find('/var/cagefs/') == -1: httpd_conf[ind] = temp # Change path to home dir in all TEXT files in /var/cpanel/userdata for next_file in os.listdir(os.path.join(USERDATA, invalid_user)): file_path = os.path.join(USERDATA, invalid_user) + '/' + next_file if is_text_file(file_path): modified = False userdata_file = open(file_path, 'r') userdata = userdata_file.readlines() userdata_file.close() for ind in range(len(userdata)): if userdata[ind].find('/var/cagefs/') != -1: temp = userdata[ind].replace(line.pw_dir, dest) # Invalid path is repaired correctly ? # line.pw_dir is NOT a part of another more long invalid path (as a result of cyclic error) ? if temp.find('/var/cagefs/') == -1: userdata[ind] = temp modified = True else: print_log(log, 'Error: cannot repair', file_path) modified = False break if modified: shutil.copyfile(file_path, file_path+'.repair.bak') print_log(log, "Created backup", file_path+'.repair.bak') userdata_file = open(file_path, 'w') for next_line in userdata: userdata_file.write(next_line) userdata_file.close() enable_user(invalid_user) print_log(log, 'User', invalid_user, "has been repaired SUCCESSFULLY!") # Write httpd.conf print_log(log, 'Writting', HTTPD_CONF, '...') _file = open(HTTPD_CONF, "w") for line in httpd_conf: _file.write(line) _file.close() # Rebuild /var/cpanel/userdata print_log(log, 'Rebuilding /var/cpanel/userdata...') run_subprocess(log, [USERDATA_UPDATE]) log.close() def print_warning(): print('Please, rename or remove /var/cagefs directory of old version of CageFS') print('in order to correct operation of new version of CageFS.') print('New /var/cagefs directory will be created automatically.') def rename_var_cagefs(): if os.path.exists('/var/cagefs'): # Users with invalid pathes to home directories do NOT exist ? if not invalid_homes_exist(): try: if os.path.isdir('/var/cagefs.old') and (not os.path.islink('/var/cagefs.old')): shutil.rmtree('/var/cagefs.old', True) else: os.unlink('/var/cagefs.old') except (OSError, IOError): pass try: os.rename('/var/cagefs', '/var/cagefs.old') print('/var/cagefs has been renamed to /var/cagefs.old') except (OSError, IOError): print_error('failed to rename /var/cagefs to /var/cagefs.old') print_warning() else: print_warning() def main(): try: opts, _ = getopt.getopt(sys.argv[1:], "f", ["do-not-ask", "rename-var-cagefs", "uninstall_cagefs_etc",\ "add-syslog-socket", "remove-syslog-socket"]) except getopt.GetoptError: print('Usage error') sys.exit(1) if (os.geteuid()!=0): print_error('root privileges required. Abort.') sys.exit(1) for o, _ in opts: if o in ('-f', '--do-not-ask'): repair_homes(False) sys.exit(0) elif o in ('--rename-var-cagefs',): rename_var_cagefs() sys.exit(0) elif o in ('--uninstall_cagefs_etc',): uninstall_cagefs_etc() sys.exit(0) elif o in ('--add-syslog-socket',): print ("Invalid option.\nUse /usr/share/cagefs-plugins/install" "-cagefs-plugin.py --add-syslog-socket") sys.exit(0) elif o in ('--remove-syslog-socket',): print ("Invalid option.\nUse /usr/share/cagefs-plugins/install" "-cagefs-plugin.py --remove-syslog-socket") sys.exit(0) repair_homes(True) # mv /home/mistersc/zthzmnvp /home/zthzmnvp # mkdir -p /var/cagefs/sc/mistersc/home/mistersc # ln -s /home/zthzmnvp /var/cagefs/sc/mistersc/home/mistersc/zthzmnvp # mv /home/lakzqyaa/zcktakme /home/zcktakme # mkdir -p /var/cagefs/aa/lakzqyaa/var/cagefs/sc/mistersc/home/mistersc/lakzqyaa # ln -s /home/zcktakme /var/cagefs/aa/lakzqyaa/var/cagefs/sc/mistersc/home/mistersc/lakzqyaa/zcktakme # OLD function (NOT USED) def repair_homes_old(): print("This script repairs home directories of users that have invalid pathes to home directories") print("(users that have path to home directory starting with /var/cagefs)") print("This script will move home directories to proper location and create") print("appropriate symlink in /var/cagefs") print("") confirm("Do you want to continue (yes/no)? ") log = open(LOGFILE, 'a+') # Print current date and time cur_time = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()) print_log(log, "*** Repair started", cur_time) if not invalid_homes_exist(): print_log(log, 'Users with invalid pathes to home directories do NOT exist') log.close() return # get all users from /etc/passwd pw = pwd.getpwall() for line in pw: if line.pw_dir.startswith('/var/cagefs/'): # pw_dir is like /var/cagefs/[prefix]/[parent-user]/home/[parent-user]/[invalid-user] # or # like /var/cagefs/[prefix]/[parent-user]/var/cagefs/[prefix2]/[parent-user2]/home/[parent-user2]/[parent-user]/[invalid-user] # etc... if os.path.islink(line.pw_dir): print_log(log, 'Home', line.pw_dir, 'for user', line.pw_name, 'is repaired already. Skipping...') continue print_log(log, "Repairing user", line.pw_name, "...") # Get name of invalid user invalid_user = os.path.basename(line.pw_dir) if invalid_user != line.pw_name: print_log(log, 'Error: Cannot repair home path', line.pw_dir, 'for user', line.pw_name) continue # Get /var/cagefs/[prefix]/[parent-user]/home/[parent-user] # or /var/cagefs/[prefix]/[parent-user]/var/cagefs/[prefix2]/[parent-user2]/home/[parent-user2]/[parent-user] var_cagefs_home_of_parent = os.path.dirname(line.pw_dir) # Get name of "parent" of invalid user parent = os.path.basename( var_cagefs_home_of_parent ) # Get passwd info for parent try: pw_line = pwd.getpwnam(parent) except Exception as e: print_log(log, 'Cannot repair home path', line.pw_dir, 'for user', line.pw_name) print_log(log, 'Error: "Parent" user', parent, 'does NOT exist') print_log(log, str(e)) continue # Get home of parent parent_home = pw_line.pw_dir if not var_cagefs_home_of_parent.endswith(parent_home): print_log(log, 'Cannot repair home path', line.pw_dir, 'for user', line.pw_name) print_log(log, 'Error: Path to home directory in /var/cagefs for parent user is invalid') print_log(log, 'Parent user:', parent) print_log(log, 'Home directory of parent user:', parent_home) print_log(log, 'Home directory of parent user in /var/cagefs:', var_cagefs_home_of_parent) continue # Get home base (parent) directory (commonly "/home") if parent_home.startswith('/var/cagefs/'): # Cyclic error. Parent user should be repaired already. So home of parent should be "/home" base_home = BASE_HOME_DIR else: base_home = os.path.dirname(parent_home) # Check that home dir of invalid user exists in home dir of "parent" user # (Check that /home/[parent-user]/[invalid-user] exists) src = base_home+'/'+parent+'/'+invalid_user if not os.path.isdir(src): print_log(log, 'Error: home directory of user', invalid_user, 'is NOT found in', base_home+'/'+parent) continue # Check that "correct" home directory of invalid_user does NOT exist yet dest = base_home+'/'+invalid_user if os.path.exists(dest): print_log(log, 'Error: home directory', dest, 'of user', invalid_user, 'already exists') continue # Do "mv /home/[parent-user]/[invalid-user] /home/[invalid-user]" try: os.rename(src, dest) except (OSError, IOError): print_log(log, 'Error while moving', src, 'to', dest) continue # Check that moving directory was successfull if (not os.path.isdir(dest)) or os.path.exists(src): print_log(log, 'Error: moving', src, 'to', dest, 'was NOT successfull') continue # Do "mkdir -p /var/cagefs/[prefix]/[parent-user]/home/[parent-user]" # or "mkdir -p /var/cagefs/[prefix]/[parent-user]/var/cagefs/[prefix2]/[parent-user2]/home/[parent-user2]/[parent-user]" # (ensure that directory exists) try: os.makedirs(var_cagefs_home_of_parent) except (OSError, IOError): pass # Do "ln -s /home/[invalid-user] /var/cagefs/[prefix]/[parent-user]/home/[parent-user]/[invalid-user]" # or "ln -s /home/[invalid-user] /var/cagefs/[prefix]/[parent-user]/var/cagefs/[prefix2]/[parent-user2]/home/[parent-user2]/[parent-user]/[invalid-user]" try: os.symlink(dest, line.pw_dir) except (OSError, IOError): print_log(log, 'Error while creatimg symlink', line.pw_dir, 'to', dest) continue print_log(log, 'User', invalid_user, "has been repaired SUCCESSFULLY!") log.close() if __name__ == "__main__": main()