HeNjV: dZddlmZddlmZddlmZddlZddlZddlZddlm Z ddl m Z m Z m Z mZddlmZmZdd lmZdd l mZdd lmZd Zd ZdZdZeddhZdZdZdZdZ ddZ!dZ"dZ#dZ$dZ%dZ&dZ'dS)u 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== {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. )print_function)division)absolute_importN) remount_proc)adminscpusers getCPName is_reseller) SysCtlConfSYSCTL_CL_CONF_FILE)Feature)is_panel_feature_supported)Clsudoadmin clsupergid clsudoersiroot lvemanager) lvemanager_cdkrdStvptfdtD}|sdS tjjt kS#t$rYdSwxYw)uReturn 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. rTc3BK|]}|VdSN) startswith).0pnames I/opt/cloudlinux/venv/lib64/python3.11/site-packages/lvemanager/sudoers.py z(_is_system_owned_user..as/ O O1DOOA   O O O O O OF) _PACKAGE_OWNED_CLSUDOERS_MEMBERSany&_PACKAGE_OWNED_CLSUDOERS_NAME_PREFIXESpwdgetpwnampw_uid_SYSTEM_UID_MAXKeyError)r is_known_names` r_is_system_owned_userr)Ss v~~t 00 P O O O O(N O O OOO u|D!!(O;; uus!A A('A(cFtjdd||g}|dkrdSdS)zAdd user to given unix group/usr/bin/gpasswdz-arFT subprocesscall user_name group_nameretcodes r_add_user_to_groupr3ms.o14JOPPG!||u 4rcFtjdd||g}|dkrdSdS)Nr+z-drFTr,r/s r_remove_user_from_groupr5vs.o14JOPPG!||u 4rc, tj|j}n#t$rYdSwxYw|sdS t t }t t }n-#t$r }td|d|Yd}~dSd}~wwxYw|std|zdSt ||z}|D]S}||vs||vr t|rn#t$rY)wxYwtd|d|dt||TdS)a6 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) NzCWARNING: could not enumerate panel admins/users to validate group 'z': zSWARNING: admins() returned no admins; skipping tenant eviction for '%s' (fail-safe)zWARNING: removing panel user 'z' from privileged group 'z ' (CLOS-4594)) grpgetgrnamgr_memr'setrr Exceptionprintr r5)r1keepmembers admin_set tenant_setekeep_setmembers r_remove_tenants_from_grouprE}s8,z**1  MM ^^  QQ  ! ! !  c   4yy9$H 4 4 X  z!9!9  6""      H  % & & & 3333 4 4s5 **6A)) B3BB C C*)C*ctt}||vr|||D]}t||t ||fdS)z Add all present DA admins (plus new_admin_name admin) to supplied group :param new_admin_name: new admin name to add :return: )r>N)listrappendr3rE)r1new_admin_name admin_listrs r_add_admins_into_grouprKsrfhhJZ''.)))..5*----z0ABBBBBBrcDtjdd|g}|dkrdSdS)zCreate group with given namez/usr/sbin/groupaddz-frFTr,)r1r2s r _create_grouprMs,o3T:FGGG!||u 4rcttjtj}t t }|d}|ds-| d|tt|dStdkrr ttjtj}n#t$rd}YnwxYw||kr-| d|tt|dS t|}n#t$rt!dwxYwtj|j}t||dS)z Add all present DA admins (plus new_admin_name admin) to current supergid group :param new_admin_name: new admin name to add :return: ) config_filezfs.proc_super_gidN DirectAdminz6Bad fs.proc_super_gid option value in /etc/sysctl.conf)strr8r9SUPER_GROUP_NAMEgr_gidr r get has_parameterr;rKr DEFAULT_GROUP_NAMEr'int ValueError RuntimeErrorgetgrgidgr_name)rI super_gid sysctl_cfgproc_super_gid admin_gidproc_super_names r_add_admins_into_supergid_grprasCL!1229::I(;<<22:O?N;;;;;s++C C&%C&D--Ec tj|n#t$rYdSwxYwt|fdjD}fdt jD}|s|rtd|d|d|dS)a? 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. Nc:g|]}|vt||Sr6)r))rmexpecteds r z/_assert_sudoers_group_clean..s< H  %:1%=%=    rcxg|]6}|jjkr$|jvt|j/|j7Sr6)pw_gidrSpw_namer))rrregrs rrfz/_assert_sudoers_group_clean..sS 8ry IX % %%ai00 & % % %rzERROR: refusing to grant %z9 passwordless sudo: group already has unexpected members z and primary-GID owners )r8r9r'r;r:r#getpwallr<)r1expected_membersunexpected_membersunexpected_primaryrerjs @@r_assert_sudoers_group_cleanros \* % % #$$H9<>> E/Ei ...0B0B DEE EEEs  ''cttjrjttst dtzt |t|tst d|dtdtt}| || ttt|ttst dtzt|tst d|dtdt}|tt#dS)NzERROR: Can't create %s group zERROR: Can't add user z to z group )rr LVErMrRr<rar3r;raddupdater roSUDOERS_GROUP_NAMEradd_lvemanager_groupr)rexpected_clsudoerssudos radd_unix_user_to_sudoersrx,sd!'+..)-.. Q??? 24FGGG + , ,O8;MMNNN d$6 7 7'i DD$$$&'' ' 88D0111NNNNNrcZt|tt|tdSr)r5rRrt)rs rremove_unix_user_from_sudoersrzRs*D"2333D"455555r)r6)(__doc__ __future__rrrr8r#r-cl_proc_hidepidrclcommon.cpapirrr r clcommon.sysctlr r clcommon.constr rclsudorrVrRrtr& frozensetr r"r)r3r5rErKrMrarorxrzr6rrrs !!F&%%%%%&&&&&& ((((((BBBBBBBBBBBB;;;;;;;;""""""555555 !$-9fl-C#D#D )9&4:4:4:4:4zCCC"$<$<$