Blob Blame History Raw
From 2a0868fccbc9f4dfc540a7d3bb5dfa22c0bdce4e Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Mon, 30 Jan 2023 14:22:30 +0200
Subject: [PATCH 1/2] ipa-kdb: PAC consistency checker needs to handle child
 domains as well

When PAC check is performed, we might get a signing TGT instead of the
client DB entry. This means it is a principal from a trusted domain but
we don't know which one exactly because we only have a krbtgt for the
forest root. This happens in MIT Kerberos 1.20 or later where KDB's
issue_pac() callback never gets the original client principal directly.

Look into known child domains as well and make pass the check if both
NetBIOS name and SID correspond to one of the trusted domains under this
forest root. Move check for the SID before NetBIOS name check because we
can use SID of the domain in PAC to find out the right child domain in
our trusted domains' topology list.

Fixes: https://pagure.io/freeipa/issue/9316

Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
---
 daemons/ipa-kdb/ipa_kdb_mspac.c | 51 +++++++++++++++++++++------------
 1 file changed, 32 insertions(+), 19 deletions(-)

diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
index a15050e2166..476d1cb558a 100644
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
@@ -1827,11 +1827,43 @@ krb5_error_code filter_logon_info(krb5_context context,
     bool result;
     char *domstr = NULL;
 
+    ipactx = ipadb_get_context(context);
+    if (!ipactx || !ipactx->mspac) {
+        return KRB5_KDB_DBNOTINITED;
+    }
+
     domain = get_domain_from_realm_update(context, realm);
     if (!domain) {
         return EINVAL;
     }
 
+    /* check exact sid */
+    result = dom_sid_check(&domain->domsid, info->info->info3.base.domain_sid, true);
+    if (!result) {
+        struct ipadb_mspac *mspac_ctx = ipactx->mspac;
+        result = FALSE;
+        /* Didn't match but perhaps the original PAC was issued by a child domain's DC? */
+        for (k = 0; k < mspac_ctx->num_trusts; k++) {
+            result = dom_sid_check(&mspac_ctx->trusts[k].domsid,
+                             info->info->info3.base.domain_sid, true);
+            if (result) {
+                domain = &mspac_ctx->trusts[k];
+                break;
+            }
+        }
+        if (!result) {
+            domstr = dom_sid_string(NULL, info->info->info3.base.domain_sid);
+            krb5_klog_syslog(LOG_ERR, "PAC Info mismatch: domain = %s, "
+                                      "expected domain SID = %s, "
+                                      "found domain SID = %s",
+                                      domain->domain_name, domain->domain_sid,
+                                      domstr ? domstr : "<failed to display>");
+            talloc_free(domstr);
+            return EINVAL;
+        }
+    }
+
+    /* At this point we may have changed the domain we look at, */
     /* check netbios/flat name */
     if (strcasecmp(info->info->info3.base.logon_domain.string,
                    domain->flat_name) != 0) {
@@ -1843,21 +1875,6 @@ krb5_error_code filter_logon_info(krb5_context context,
         return EINVAL;
     }
 
-    /* check exact sid */
-    result = dom_sid_check(&domain->domsid, info->info->info3.base.domain_sid, true);
-    if (!result) {
-        domstr = dom_sid_string(NULL, info->info->info3.base.domain_sid);
-        if (!domstr) {
-            return EINVAL;
-        }
-        krb5_klog_syslog(LOG_ERR, "PAC Info mismatch: domain = %s, "
-                                  "expected domain SID = %s, "
-                                  "found domain SID = %s",
-                                  domain->domain_name, domain->domain_sid, domstr);
-        talloc_free(domstr);
-        return EINVAL;
-    }
-
     /* Check if this domain has been filtered out by the trust itself*/
     if (domain->parent != NULL) {
         for(k = 0; k < domain->parent->len_sid_blocklist_incoming; k++) {
@@ -1944,10 +1961,6 @@ krb5_error_code filter_logon_info(krb5_context context,
      * should include different possibilities into account
      * */
     if (info->info->info3.sidcount != 0) {
-        ipactx = ipadb_get_context(context);
-        if (!ipactx || !ipactx->mspac) {
-            return KRB5_KDB_DBNOTINITED;
-        }
         count = info->info->info3.sidcount;
         i = 0;
         j = 0;

From 1a4f2597253c750696f6cd34613b375dc30fe456 Mon Sep 17 00:00:00 2001
From: Anuja More <amore@redhat.com>
Date: Mon, 30 Jan 2023 19:27:49 +0530
Subject: [PATCH 2/2] Add test for SSH with GSSAPI auth.

Added test for aduser with GSSAPI authentication.

Related : https://pagure.io/freeipa/issue/9316

Signed-off-by: Anuja More <amore@redhat.com>
---
 ipatests/test_integration/test_trust.py | 46 +++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/ipatests/test_integration/test_trust.py b/ipatests/test_integration/test_trust.py
index 21c814ff1a9..a1ed906c6ae 100644
--- a/ipatests/test_integration/test_trust.py
+++ b/ipatests/test_integration/test_trust.py
@@ -527,6 +527,35 @@ def test_subordinate_suffix(self):
                    .format(self.ad_domain, subordinate_suffix))
             self.ad.run_command(['powershell', '-c', cmd])
 
+    def test_ssh_aduser(self):
+        """Test ssh with GSSAPI is working with aduser
+
+        When kerberos ticket is obtained for child domain user
+        and ssh with this ticket should be successful
+        with no password prompt.
+
+        Related : https://pagure.io/freeipa/issue/9316
+        """
+        testuser = 'testuser@{0}'.format(self.ad_domain)
+        testusersub = 'subdomaintestuser@{0}'.format(self.ad_subdomain)
+
+        def sshuser(host, user):
+            tasks.kdestroy_all(host)
+            try:
+                tasks.kinit_as_user(host, user,
+                                    host.config.ad_admin_password
+                                    )
+                ssh_cmd = "ssh -q -K -l {user} {host} hostname"
+                valid_ssh = host.run_command(
+                    ssh_cmd.format(user=user, host=host.hostname)
+                )
+                assert host.hostname in valid_ssh.stdout_text
+            finally:
+                tasks.kdestroy_all(host)
+
+        sshuser(self.master, testuser)
+        sshuser(self.master, testusersub)
+
     def test_remove_nonposix_trust(self):
         self.remove_trust(self.ad)
         tasks.unconfigure_dns_for_trust(self.master, self.ad)
@@ -784,6 +813,23 @@ def test_user_gid_uid_resolution_in_external_treedomain_trust(self):
         assert re.search(
             testuser_regex, result.stdout_text), result.stdout_text
 
+    def test_ssh_adtreeuser(self):
+        testuser = 'treetestuser@{0}'.format(self.ad_treedomain)
+        self.master.run_command(["id", testuser])
+        tasks.clear_sssd_cache(self.master)
+        tasks.kdestroy_all(self.master)
+        try:
+            tasks.kinit_as_user(self.master, testuser,
+                                password="Secret123456"
+                                )
+            ssh_cmd = "ssh -q -K -l {user} {host} hostname"
+            valid_ssh = self.master.run_command(
+                ssh_cmd.format(user=testuser, host=self.master.hostname)
+            )
+            assert self.master.hostname in valid_ssh.stdout_text
+        finally:
+            tasks.kdestroy_all(self.master)
+
     def test_remove_external_treedomain_trust(self):
         self.remove_trust(self.tree_ad)
         tasks.unconfigure_dns_for_trust(self.master, self.ad, self.tree_ad)