403Webshell
Server IP : 142.11.234.102  /  Your IP : 216.73.217.78
Web Server : Apache
System : Linux dal-shared-66.hostwindsdns.com 4.18.0-513.24.1.lve.1.el8.x86_64 #1 SMP Thu May 9 15:10:09 UTC 2024 x86_64
User : krnuyqrm ( 1183)
PHP Version : 7.4.33
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /opt/cloudlinux/venv/lib/python3.11/site-packages/lvemanager/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /opt/cloudlinux/venv/lib/python3.11/site-packages/lvemanager/sudoers.py
# 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
"""
clsudoers unix group management for lvemanager.

Operator upgrade note (CLOS-4576, F-37): ``add_unix_user_to_sudoers()`` now
fails closed when the ``clsudoers`` unix group has pre-existing members
outside the legitimate set (``admins()`` + the user being added +
package-owned members ``root`` / ``lvemanager``). Previously,
``groupadd -f`` was silently idempotent and any stowaway primary-GID
owners or ``gr_mem`` entries (left by manual admin steps or another
RPM's post-install) were quietly elevated to passwordless sudo for
selectorctl / cloudlinux-selector.

The allowlist explicitly includes ``root`` (implicit group membership in
many tools) and ``lvemanager`` (the package's runtime user, placed in
``clsudoers`` by the lvemanager RPM's ``%post`` for SPA's clsudo flow on
DirectAdmin/Plesk) — so the gate fires only on the F-37 attack signature
(a stowaway secondary member or a primary-GID-reuse owner), not on
legitimate package-installed state.

After upgrading to a build that ships this change, the first admin-add
operation on an AFFECTED host (one with non-package stowaways in the
group) raises an exception from ``_assert_sudoers_group_clean()``. To
recover, operators must either:

  * remove the stowaway entries from the ``clsudoers`` group (audit with
    ``getent group clsudoers`` and ``awk -F: '$4==<gid> {print $1}' /etc/passwd``
    for primary-GID owners), OR
  * add those users via the panel's ``admins()`` interface (panel admin user
    list) so they are recognised as legitimate members before retrying.

This is intentional — silent elevation of unaudited accounts is the
vulnerability being closed; reverting to ``groupadd -f`` idempotence is not
an acceptable workaround.
"""

from __future__ import print_function
from __future__ import division
from __future__ import absolute_import

import grp
import pwd
import subprocess

from cl_proc_hidepid import remount_proc
from clcommon.cpapi import admins, cpusers, getCPName, is_reseller
from clcommon.sysctl import SysCtlConf, SYSCTL_CL_CONF_FILE
from clcommon.const import Feature
from clcommon.cpapi import is_panel_feature_supported
from clsudo import Clsudo

# Default admins group
DEFAULT_GROUP_NAME = "admin"

# Group name for fs.proc_super_gid
SUPER_GROUP_NAME = "clsupergid"

# Groupname for sudoers
SUDOERS_GROUP_NAME = "clsudoers"

# System-UID ceiling: an upper bound on the UID an account may have to be
# considered "package-installed" (`useradd --system`). The allowlist below
# additionally requires the account name to belong to the lvemanager
# package family — UID range alone is NOT the gate; it is the SECOND
# precondition. Hard-coding 999 is safe across CL7/8/9/10 and ubuntu22.
_SYSTEM_UID_MAX = 999

# Always-allowed package-owned member names. The lvemanager package's
# canonical SPA runtime user is `lvemanager`. `root` is implicit / always
# benign. DirectAdmin installs sometimes append a UUID suffix
# (`lvemanager_<uuid>`); the helper below matches that family by name
# prefix AND requires the matched account to live in the system-UID
# range. Anything else (a `daemon`/`bin`/`mail` system account, a
# regular user, a stowaway with a fabricated name) fails closed.
_PACKAGE_OWNED_CLSUDOERS_MEMBERS = frozenset({"root", "lvemanager"})
_PACKAGE_OWNED_CLSUDOERS_NAME_PREFIXES = ("lvemanager_",)


def _is_system_owned_user(name):
    """Return True if `name` is a known package-owned `clsudoers` member
    (always-allowed name OR an lvemanager-package-family name) AND lives
    in the system-UID range. Both conditions are required — a regular
    user named `lvemanager_evil` (UID 5000) is NOT package-owned; an
    unrelated system account named `daemon` (UID 2) is NOT package-owned
    either. The gate exists to fire on the F-37 priv_group_reuse
    signature; we only want to exempt the RPM's own post-install state.
    """
    if name == "root":
        # Implicit group membership in many tools; never a stowaway.
        return True
    is_known_name = (
        name in _PACKAGE_OWNED_CLSUDOERS_MEMBERS or
        any(name.startswith(p) for p in _PACKAGE_OWNED_CLSUDOERS_NAME_PREFIXES)
    )
    if not is_known_name:
        return False
    try:
        return pwd.getpwnam(name).pw_uid <= _SYSTEM_UID_MAX
    except KeyError:
        # gr_mem references a non-existent user — that itself is suspicious
        # (stowaway entry); let the gate flag it.
        return False


def _add_user_to_group(user_name, group_name):
    """Add user to given unix group"""
    retcode = subprocess.call(["/usr/bin/gpasswd", "-a", user_name, group_name])
    if retcode != 0:
        return False
    return True


# Remove user from group
def _remove_user_from_group(user_name, group_name):
    retcode = subprocess.call(["/usr/bin/gpasswd", "-d", user_name, group_name])
    if retcode != 0:
        return False
    return True


def _remove_tenants_from_group(group_name, keep=()):
    """
    Remove panel end-users (tenants) from a privileged group.

    CLOS-4594 (F-54/F-56): membership of the fs.proc_super_gid group (clsupergid) is
    privileged and must be limited to legitimate elevated actors. lve-utils
    (clsupergid_process / clsetuplib) may reuse a pre-existing group of that name, so we
    reconcile its membership here. lve-utils cannot tell a legitimate panel admin
    (a regular uid >= UID_MIN login) from a tenant; lvemanager can, because it knows the
    authoritative cpapi sets. Here we remove only CONFIRMED panel end-users
    (cpapi.cpusers() that are neither admins nor resellers) -- a tenant must never be a
    member. This deliberately does NOT use a uid threshold (which is what made the
    earlier in-tool attempt false-abort on real cPanel/DA/Plesk hosts by flagging legitimate
    panel admins as untrusted).

    Fail-safe rules (must never strip a legitimate elevated actor):
      * If cpapi enumeration raises, OR admins() returns an EMPTY set, do nothing and return.
        An empty admin set cannot be distinguished from a failed-open read (e.g. DirectAdmin
        admins() returns set() without raising when admin.list is empty/truncated mid-write,
        while DA admins are simultaneously listed by cpusers()); reconciling against it would
        strip every real admin from the group -- the very regression this fix exists to avoid.
      * Admins (admins()), the just-added admin (keep), and resellers (is_reseller()) are
        elevated actors and are never removed. Service accounts (nagios/mailman/polkitd, the
        lvemanager system user, ...) are not panel users, so they are never candidates.

    :param group_name: the group to reconcile (the configured proc-super group)
    :param keep: extra usernames to preserve regardless (e.g. the admin being added right now)
    """
    try:
        members = grp.getgrnam(group_name).gr_mem
    except KeyError:
        return
    if not members:
        return
    try:
        admin_set = set(admins())
        tenant_set = set(cpusers())
    except Exception as e:  # cpapi failure must not break group setup
        print("WARNING: could not enumerate panel admins/users to validate group '%s': %s"
              % (group_name, e))
        return
    if not admin_set:
        # Refuse to reconcile against an empty admin set: it is indistinguishable from a
        # failed-open admins() read, and evicting would strip real admins (CLOS-4594).
        print("WARNING: admins() returned no admins; skipping tenant eviction for '%s' (fail-safe)"
              % group_name)
        return
    keep_set = set(keep) | admin_set
    for member in members:
        if member in keep_set or member not in tenant_set:
            continue
        try:
            if is_reseller(member):
                continue  # resellers are elevated actors, not tenants
        except Exception:
            continue  # cannot confirm role -> never remove (fail-safe)
        print("WARNING: removing panel user '%s' from privileged group '%s' (CLOS-4594)"
              % (member, group_name))
        _remove_user_from_group(member, group_name)


def _add_admins_into_group(group_name, new_admin_name):
    """
    Add all present DA admins (plus new_admin_name admin) to supplied group
    :param new_admin_name: new admin name to add
    :return:
    """
    # Get admin list from DA and append new admin name to it
    admin_list = list(admins())
    if new_admin_name not in admin_list:
        admin_list.append(new_admin_name)
    for admin in admin_list:
        _add_user_to_group(admin, group_name)
    # CLOS-4594 (F-54/F-56): the proc-super group is privileged;
    # evict any tenant that slipped in (e.g. via a reused pre-existing group).
    _remove_tenants_from_group(group_name, keep=(new_admin_name,))


def _create_group(group_name):
    """Create group with given name"""
    retcode = subprocess.call(["/usr/sbin/groupadd", "-f", group_name])
    if retcode != 0:
        return False
    return True


def _add_admins_into_supergid_grp(new_admin_name):
    """
    Add all present DA admins (plus new_admin_name admin) to current supergid group
    :param new_admin_name: new admin name to add
    :return:
    """
    # Determine SUPER_GROUP_NAME gid
    super_gid = str(grp.getgrnam(SUPER_GROUP_NAME).gr_gid)

    sysctl_cfg = SysCtlConf(config_file=SYSCTL_CL_CONF_FILE)

    # returns set gid from sysctl.conf or kernel default
    proc_super_gid = sysctl_cfg.get('fs.proc_super_gid')
    # set fs.proc_super_gid and add admins to group with this gid if:
    #  1. it was not found in sysctl.conf;
    if not sysctl_cfg.has_parameter('fs.proc_super_gid'):
        sysctl_cfg.set('fs.proc_super_gid', super_gid)
        _add_admins_into_group(SUPER_GROUP_NAME, new_admin_name)
        return
    elif getCPName() == 'DirectAdmin':
        # Only for DA
        try:
            admin_gid = str(grp.getgrnam(DEFAULT_GROUP_NAME).gr_gid)
        except KeyError:
            admin_gid = None
        if proc_super_gid == admin_gid:
            sysctl_cfg.set('fs.proc_super_gid', super_gid)
            _add_admins_into_group(SUPER_GROUP_NAME, new_admin_name)
            return
    # otherwise read fs.proc_super_gid and add admins to group with this gid
    try:
        proc_super_gid = int(proc_super_gid)
    except ValueError:
        raise RuntimeError("Bad fs.proc_super_gid option value in /etc/sysctl.conf")
    # add all panel admins into custom proc_super_gid group
    proc_super_name = grp.getgrgid(proc_super_gid).gr_name
    _add_admins_into_group(proc_super_name, new_admin_name)


def _assert_sudoers_group_clean(group_name, expected_members):
    """
    Refuse to grant %group_name passwordless sudo if the unix group already
    exists with members or primary-GID owners that the lvemanager flow did
    not place there. `groupadd -f` is silently idempotent and would otherwise
    elevate any pre-existing member on the first call.

    :param group_name: target group (clsudoers)
    :param expected_members: iterable of usernames the lvemanager flow is
        about to authorize (panel admins + the user being added).
    :raises Exception: if any unexpected secondary member or primary-GID
        owner is found.
    """
    try:
        gr = grp.getgrnam(group_name)
    except KeyError:
        # Group does not exist yet — _create_group will create it empty.
        return
    expected = set(expected_members)
    # Secondary `gr_mem` entries that are neither expected (panel admins +
    # the user being added) nor package-installed system accounts are the
    # F-37 stowaway signature. The system-account allowlist covers
    # legitimate runtime users the lvemanager RPM (and DA-side installers)
    # place in the group at install time, including DA variants like
    # `lvemanager_<uuid>` which still land in the UID system range.
    unexpected_members = [
        m for m in gr.gr_mem
        if m not in expected and not _is_system_owned_user(m)
    ]
    # Primary-GID owners are the canonical F-37 attack: a user account
    # whose primary group landed on clsudoers.gr_gid via GID-reuse or a
    # buggy installer. Only the lvemanager-package family of system
    # accounts is legitimately allowed to have clsudoers as a primary
    # group; an unrelated system user with the same primary GID is the
    # priv_group_reuse signature and must trip the gate. Reuse the
    # `_is_system_owned_user` predicate so the name + UID criteria stay
    # in lockstep with the gr_mem path above.
    unexpected_primary = [
        p.pw_name for p in pwd.getpwall()
        if p.pw_gid == gr.gr_gid
        and p.pw_name not in expected
        and not _is_system_owned_user(p.pw_name)
    ]
    if unexpected_members or unexpected_primary:
        raise Exception(
            "ERROR: refusing to grant %%%s passwordless sudo: group already "
            "has unexpected members %r and primary-GID owners %r" % (
                group_name, unexpected_members, unexpected_primary))


def add_unix_user_to_sudoers(name):
    # create all supergid stuff only if regular CL edition
    if is_panel_feature_supported(Feature.LVE):
        if not _create_group(SUPER_GROUP_NAME):
            raise Exception("ERROR: Can't create %s group\n" % SUPER_GROUP_NAME)

        _add_admins_into_supergid_grp(name)

        if not _add_user_to_group(name, SUPER_GROUP_NAME):
            raise Exception("ERROR: Can't add user %s to %s group\n" % (
                name, SUPER_GROUP_NAME))

    # Gate: if clsudoers already exists with non-lvemanager members, fail
    # closed rather than silently elevate them via Clsudo.add_lvemanager_group.
    # Allowlist members the lvemanager RPM itself places in the group at
    # package install time (see _PACKAGE_OWNED_CLSUDOERS_MEMBERS) so the
    # gate fires only on the F-37 attack signature, not on legitimate
    # package-installed state.
    expected_clsudoers = set(admins())
    expected_clsudoers.add(name)
    expected_clsudoers.update(_PACKAGE_OWNED_CLSUDOERS_MEMBERS)
    _assert_sudoers_group_clean(SUDOERS_GROUP_NAME, expected_clsudoers)

    if not _create_group(SUDOERS_GROUP_NAME):
        raise Exception("ERROR: Can't create %s group\n" % SUDOERS_GROUP_NAME)

    if not _add_user_to_group(name, SUDOERS_GROUP_NAME):
        raise Exception("ERROR: Can't add user %s to %s group\n" % (
            name, SUDOERS_GROUP_NAME))

    # Add SUDOERS_GROUP_NAME group to /etc/sudoers
    sudo = Clsudo()
    sudo.add_lvemanager_group(SUDOERS_GROUP_NAME)

    # CAG-796: use hidepid=2 when mounting /proc
    remount_proc()


def remove_unix_user_from_sudoers(name):
    # Remove user from all groups
    _remove_user_from_group(name, SUPER_GROUP_NAME)
    _remove_user_from_group(name, SUDOERS_GROUP_NAME)

Youez - 2016 - github.com/yon3zu
LinuXploit