public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/freeipa] f44: Fine-tune permissions checks
@ 2026-09-07 13:21 Alexander Bokovoy
  0 siblings, 0 replies; only message in thread
From: Alexander Bokovoy @ 2026-09-07 13:21 UTC (permalink / raw)
  To: git-commits

            A new commit has been pushed.

            Repo   : rpms/freeipa
            Branch : f44
            Commit : f7603c31f56a77ed17a81320ded2bcb3043fd583
            Author : Alexander Bokovoy <abokovoy@redhat.com>
            Date   : 2026-09-07T16:21:19+03:00
            Stats  : +123/-1 in 2 file(s)
            URL    : https://src.fedoraproject.org/rpms/freeipa/c/f7603c31f56a77ed17a81320ded2bcb3043fd583?branch=f44

            Log:
            Fine-tune permissions checks

Upstream PR#8558: https://github.com/freeipa/freeipa/pull/8558

Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>

---
diff --git a/freeipa-pr-8558.patch b/freeipa-pr-8558.patch
new file mode 100644
index 0000000..a9aa494
--- /dev/null
+++ b/freeipa-pr-8558.patch
@@ -0,0 +1,118 @@
+From 3c37b5ab9bdb0d1788180b586b5f814535d892cf Mon Sep 17 00:00:00 2001
+From: Alexander Bokovoy <abokovoy@redhat.com>
+Date: Mon, 7 Sep 2026 16:12:33 +0300
+Subject: [PATCH] Allow fine tuned privilege check
+
+Let users pass through the privilege check if either
+- whole entry read granted ('v' in the GER response)
+- at least one attribute read granted
+
+This is enough to allow LDAPRetrieve to pass, because the rest is
+controlled by the explicit ACIs in LDAP.
+
+Related: CVE-2026-79678
+
+Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
+---
+ ipaserver/plugins/baseldap.py  | 27 ++++++++++++++++++++++-----
+ ipaserver/plugins/privilege.py | 29 +++++++++++++++++++++--------
+ 2 files changed, 43 insertions(+), 13 deletions(-)
+
+diff --git a/ipaserver/plugins/baseldap.py b/ipaserver/plugins/baseldap.py
+index d693c7112..e751dc673 100644
+--- a/ipaserver/plugins/baseldap.py
++++ b/ipaserver/plugins/baseldap.py
+@@ -1054,11 +1054,22 @@ last, after all sets and adds."""),
+                 if not attrs:
+                     return True
+                 return any('w' in attr_rights.get(a, '') for a in attrs)
+-            # read / search / compare
+-            attrs = {a.lower() for a in perm.get('ipapermdefaultattr', ())}
+-            if not attrs:
++            # read / search / compare: a read is never denied wholesale by
++            # 389-ds -- it returns the entry and silently filters out the
++            # individual attributes the caller may not see.  Permit the
++            # operation whenever the caller can view the entry (entry-level
++            # 'v') or read *any* of its attributes.  IPA's read ACIs are
++            # attribute-scoped, so an unprivileged caller reading its own entry
++            # gets 'entryLevelRights: none' yet can still read cn/sn/mail/...;
++            # requiring read on this permission's specific attributes would
++            # wrongly turn such a legitimate partial read into a total denial.
++            # Attribute-level confidentiality (e.g. the admin-only Kerberos
++            # login attributes) stays enforced by 389-ds on the read that
++            # follows.  The probe always requests '*' (see
++            # _probe_effective_rights), so attr_rights reflects every attribute.
++            if 'v' in entry_rights:
+                 return True
+-            return any('r' in attr_rights.get(a, '') for a in attrs)
++            return any('r' in r for r in attr_rights.values())
+ 
+         modified_attrs = None  # computed lazily, only for 'write'
+         for m in mp.keys():
+@@ -1214,8 +1225,14 @@ last, after all sets and adds."""),
+                 continue
+             needed.update(a.lower() for a in perm.get('ipapermdefaultattr', ()))
+ 
++        # Always include '*' so the effective rights cover every attribute of
++        # the entry, not only the gated ones.  The read/search/compare check
++        # needs to know whether the caller can read *any* attribute (an
++        # unprivileged caller may read its own entry without holding the
++        # object's read privilege), which cannot be answered from the gated
++        # attributes alone.
+         try:
+-            rights = ldap.get_effective_rights(dn, sorted(needed) or ['*'])
++            rights = ldap.get_effective_rights(dn, sorted(needed | {'*'}))
+         except errors.NotFound:
+             return None
+ 
+diff --git a/ipaserver/plugins/privilege.py b/ipaserver/plugins/privilege.py
+index 47f27f12c..65199adc0 100644
+--- a/ipaserver/plugins/privilege.py
++++ b/ipaserver/plugins/privilege.py
+@@ -92,11 +92,28 @@ def principal_has_privilege(api, principal, privilege):
+     privilege_dn = api.Object.privilege.get_dn(privilege)
+     ldap = api.Backend.ldap2
+     if principal is None:
+-        dn_or_princ = DN(ldap.conn.whoami_s()[4:])
+-        if dn_or_princ == DN('cn=Directory Manager'):
++        # whoami reports the bound identity as an entry DN, not a Kerberos
++        # principal name. Check its privilege membership directly by DN: a
++        # 'krbprincipalname=<DN>' filter can never match and would always
++        # (wrongly) report the caller as lacking the privilege.
++        bound_dn = DN(ldap.conn.whoami_s()[4:])
++        if bound_dn == DN('cn=Directory Manager'):
+             return True
+-    else:
+-        dn_or_princ = principal
++        filter = ldap.make_filter(
++            {'memberof': privilege_dn}, rules=ldap.MATCH_ALL)
++        try:
++            ldap.find_entries(base_dn=bound_dn, scope=ldap.SCOPE_BASE,
++                              filter=filter)
++            return True
++        except errors.ExecutionError:
++            # NotFound is the normal negative result (not a member, or the
++            # bound entry does not exist). Any other execution error (database
++            # error, limits, ...) cannot positively confirm the privilege
++            # either, and there is no Kerberos principal to fall back on for
++            # the ID override check below, so fail closed: not privileged.
++            return False
++
++    dn_or_princ = principal
+ 
+     # First try: Check if there is a principal that has the needed
+     # privilege.
+@@ -110,10 +127,6 @@ def principal_has_privilege(api, principal, privilege):
+     except errors.NotFound:
+         pass
+ 
+-    # Do not run ID override check for the user that has no Kerberos principal
+-    if principal is None:
+-        return False
+-
+     # Second try: Check if there is an idoverride for the principal as
+     # ipaOriginalUid that has the needed privilege.
+     filter = ldap.make_filter(
+-- 
+2.55.0
+

diff --git a/freeipa.spec b/freeipa.spec
index 9dfc1ad..0364fa9 100644
--- a/freeipa.spec
+++ b/freeipa.spec
@@ -211,7 +211,7 @@
 
 Name:           %{package_name}
 Version:        %{IPA_VERSION}
-Release:        1.1%{?rc_version:.%rc_version}%{?dist}
+Release:        1.2%{?rc_version:.%rc_version}%{?dist}
 Summary:        The Identity, Policy and Audit system
 
 License:        GPL-3.0-or-later
@@ -236,6 +236,7 @@ Source2:        gpgkey-B40A78FBA576C4A3FC7D7BBC359FAF777296F653.asc
 
 Patch0:         freeipa-version-upgrade-fedora-only.patch
 Patch1:         freeipa-pr-8557.patch
+Patch2:         freeipa-pr-8558.patch
 
 # RHEL spec file only: START: Change branding to IPA and Identity Management
 # Moved branding logos and background to redhat-logos-ipa-80.4:
@@ -1975,6 +1976,9 @@ fi
 %endif
 
 %changelog
+* Mon Sep 07 2026 Alexander Bokovoy <abokovoy@redhat.com> - 4.13.4-1.2
+- Fine-tune privilege checks (upstream PR 8558)
+
 * Mon Sep 07 2026 Alexander Bokovoy <abokovoy@redhat.com> - 4.13.4-1.1
 - Fix cross-forest trust identity confusion protection (upstream PR 8557)
 

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-09-07 13:21 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-07 13:21 [rpms/freeipa] f44: Fine-tune permissions checks Alexander Bokovoy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox