From 26047f07c0f7aa61a44543de8674ec7d0904812e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Thu, 10 Aug 2023 13:16:51 +0200
Subject: [PATCH] ipa: do not go offline if group does not have SID
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This happens during applying overrides on cached group
during initgroups of trusted user. If the group does not
have SID (it's GID is outside the sidgen range), SSSD goes
offline.

Only SSSD running in server_mode is affected.

This patch ignores error in single group and rather continues
processing the remaining groups.

Resolves: https://github.com/SSSD/sssd/issues/6942

Reviewed-by: Sumit Bose <sbose@redhat.com>
Reviewed-by: Tomáš Halman <thalman@redhat.com>

Reference: https://github.com/SSSD/sssd/commit/26047f07c0f7aa61a44543de8674ec7d0904812e 
Conflict: NA
---
 src/providers/ipa/ipa_id.c                    | 97 +++++++++----------
 src/tests/system/tests/test_trust_identity.py | 61 ++++++++++++
 2 files changed, 109 insertions(+), 49 deletions(-)
 create mode 100644 src/tests/system/tests/test_trust_identity.py

diff --git a/src/providers/ipa/ipa_id.c b/src/providers/ipa/ipa_id.c
index 636e07965..fcac56ce2 100644
--- a/src/providers/ipa/ipa_id.c
+++ b/src/providers/ipa/ipa_id.c
@@ -291,66 +291,65 @@ static int ipa_initgr_get_overrides_step(struct tevent_req *req)
     int ret;
     struct tevent_req *subreq;
     const char *ipa_uuid;
+    const char *dn;
     struct ipa_initgr_get_overrides_state *state = tevent_req_data(req,
                                         struct ipa_initgr_get_overrides_state);
 
-    DEBUG(SSSDBG_TRACE_LIBS,
-          "Processing group %zu/%zu\n", state->group_idx, state->group_count);
+    for (; state->group_idx < state->group_count; state->group_idx++) {
+        dn = ldb_dn_get_linearized(state->groups[state->group_idx]->dn);
 
-    if (state->group_idx >= state->group_count) {
-        return EOK;
-    }
+        DEBUG(SSSDBG_TRACE_LIBS, "Processing group %s (%zu/%zu)\n",
+              dn, state->group_idx, state->group_count);
 
-    ipa_uuid = ldb_msg_find_attr_as_string(state->groups[state->group_idx],
-                                           state->groups_id_attr, NULL);
-    if (ipa_uuid == NULL) {
-        /* This should never happen, the search filter used to get the list
-         * of groups includes "uuid=*"
-         */
-        DEBUG(SSSDBG_OP_FAILURE,
-              "The group %s has no UUID attribute %s, error!\n",
-              ldb_dn_get_linearized(state->groups[state->group_idx]->dn),
-              state->groups_id_attr);
-        return EINVAL;
-    }
+        ipa_uuid = ldb_msg_find_attr_as_string(state->groups[state->group_idx],
+                                               state->groups_id_attr, NULL);
+        if (ipa_uuid == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "The group %s has no UUID attribute %s, error!\n",
+                  dn, state->groups_id_attr);
+            continue;
+        }
 
-    talloc_free(state->ar); /* Avoid spiking memory with many groups */
+        talloc_free(state->ar); /* Avoid spiking memory with many groups */
 
-    if (strcmp(state->groups_id_attr, SYSDB_UUID) == 0) {
-        ret = get_dp_id_data_for_uuid(state, ipa_uuid,
-                                       state->user_dom->name, &state->ar);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_OP_FAILURE, "get_dp_id_data_for_sid failed.\n");
-            return ret;
-        }
-    } else if (strcmp(state->groups_id_attr, SYSDB_SID_STR) == 0) {
-        ret = get_dp_id_data_for_sid(state, ipa_uuid,
-                                      state->user_dom->name, &state->ar);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_OP_FAILURE, "get_dp_id_data_for_sid failed.\n");
-            return ret;
+        if (strcmp(state->groups_id_attr, SYSDB_UUID) == 0) {
+            ret = get_dp_id_data_for_uuid(state, ipa_uuid,
+                                          state->user_dom->name, &state->ar);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE, "get_dp_id_data_for_sid failed.\n");
+                return ret;
+            }
+        } else if (strcmp(state->groups_id_attr, SYSDB_SID_STR) == 0) {
+            ret = get_dp_id_data_for_sid(state, ipa_uuid,
+                                         state->user_dom->name, &state->ar);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE, "get_dp_id_data_for_sid failed.\n");
+                return ret;
+            }
+        } else {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported groups ID type [%s].\n",
+                  state->groups_id_attr);
+            return EINVAL;
         }
-    } else {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported groups ID type [%s].\n",
-                                   state->groups_id_attr);
-        return EINVAL;
-    }
 
-    DEBUG(SSSDBG_TRACE_LIBS, "Fetching group %s\n", ipa_uuid);
+        DEBUG(SSSDBG_TRACE_LIBS, "Fetching group %s: %s\n", dn, ipa_uuid);
 
-    subreq = ipa_get_ad_override_send(state, state->ev,
-                                      state->ipa_ctx->sdap_id_ctx,
-                                      state->ipa_ctx->ipa_options,
-                                      state->realm,
-                                      state->ipa_ctx->view_name,
-                                      state->ar);
-    if (subreq == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "ipa_get_ad_override_send failed.\n");
-        return ENOMEM;
+        subreq = ipa_get_ad_override_send(state, state->ev,
+                                          state->ipa_ctx->sdap_id_ctx,
+                                          state->ipa_ctx->ipa_options,
+                                          state->realm,
+                                          state->ipa_ctx->view_name,
+                                          state->ar);
+        if (subreq == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "ipa_get_ad_override_send failed.\n");
+            return ENOMEM;
+        }
+        tevent_req_set_callback(subreq,
+                                ipa_initgr_get_overrides_override_done, req);
+        return EAGAIN;
     }
-    tevent_req_set_callback(subreq,
-                            ipa_initgr_get_overrides_override_done, req);
-    return EAGAIN;
+
+    return EOK;
 }
 
 static void ipa_initgr_get_overrides_override_done(struct tevent_req *subreq)
diff --git a/src/tests/system/tests/test_trust_identity.py b/src/tests/system/tests/test_trust_identity.py
new file mode 100644
index 000000000..9076b8724
--- /dev/null
+++ b/src/tests/system/tests/test_trust_identity.py
@@ -0,0 +1,61 @@
+"""
+Identity of trusted users and groups.
+
+:requirement: IDM-SSSD-REQ: Testing SSSD in IPA Provider
+"""
+
+from __future__ import annotations
+
+import pytest
+from sssd_test_framework.roles.generic import GenericADProvider
+from sssd_test_framework.roles.ipa import IPA
+from sssd_test_framework.topology import KnownTopologyGroup
+
+
+@pytest.mark.importance("low")
+@pytest.mark.ticket(jira="RHEL-3925", gh=6942)
+@pytest.mark.topology(KnownTopologyGroup.IPATrust)
+def test_trust_identity__group_without_sid(ipa: IPA, trusted: GenericADProvider):
+    """
+    :title: Subdomain goes offline if IPA group is missing SID
+    :setup:
+        1. Create IPA external group "external-group" and add AD user "Administrator" as a member
+        2. Create IPA posix group "posix-group" and add "external-group" as a member
+        3. Clear SSSD cache and logs on IPA server
+        4. Restart SSSD on IPA server
+    :steps:
+        1. Resolve user "Administrator@addomain"
+        2. Expire user "Administrator@addomain"
+        3. Resolve user "Administrator@addomain"
+        4. Run "sssctl domain-status addomain"
+    :expectedresults:
+        1. User is resolved and member of posix-group
+        2. User is expired in SSSD cache
+        3. User is resolved and member of posix-group
+        4. The Active Directory domain is still online
+    :customerscenario: True
+    """
+    username = trusted.fqn("administrator")
+    external = ipa.group("external-group").add(external=True).add_member(username)
+    ipa.group("posix-group").add(gid=5001).add_member(external)
+
+    ipa.sssd.clear(db=True, memcache=True, logs=True)
+    ipa.sssd.restart()
+
+    # Cache trusted user
+    result = ipa.tools.id(username)
+    assert result is not None
+    assert result.user.name == username
+    assert result.memberof("posix-group")
+
+    # Expire the user and resolve it again, this will trigger the affected code path
+    ipa.sssctl.cache_expire(user=username)
+    result = ipa.tools.id(username)
+    assert result is not None
+    assert result.user.name == username
+    assert result.memberof("posix-group")
+
+    # Check that SSSD did not go offline
+    status = ipa.sssctl.domain_status(trusted.domain, online=True)
+    assert "online status: offline" not in status.stdout.lower()
+    assert "online status: online" in status.stdout.lower()
-- 
2.33.0

