Mini Shell

Direktori : /usr/share/cagefs/
Upload File :
Current File : //usr/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()

Zerion Mini Shell 1.0