On Thu, 03 Sep 2015, Alexander Bokovoy wrote:
Hi,

attached patch adds support for issuing client referrals when FreeIPA
KDC is asked to give a TGT for a principal from a trusted forest.

We return a matching forest name as a realm and KDC then returns an
error pointing a client to a direction of that realm. You can see how it
looks with http://fpaste.org/263064/14412849/ -- it shows behavior for
both 'kinit -E -C' and 'kinit -E'.

Note that current MIT Kerberos KDC has a bug that prevents us from
responding with a correct client referral. A patched version for Fedora
22 is available in COPR abbra/krb5-test, a fix to upstream krb5 is
https://github.com/krb5/krb5/pull/323/ and I'm working on filing bugs to
Fedora and RHEL versions.

With the version in my abbra/krb5-test COPR you can test the patch with
the help of kinit like fpaste URL above shows.
After discussing with Simo and Sumit, here is updated patch that
operates directly on 'search_for' krb5_principal and avoids
strchr()/strrchr() and additional memory allocations -- it uses
memrchr() to find '@' in the last component of the search_for principal
and considers the part of the component after '@' as an enterprise realm
to check.

--
/ Alexander Bokovoy
From af2ce7db9c51b7b058c5077801416f2757eb4896 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <aboko...@redhat.com>
Date: Thu, 20 Aug 2015 15:06:12 +0300
Subject: [PATCH] client referral support for trusted domain principals

https://fedorahosted.org/freeipa/ticket/3559
---
 daemons/ipa-kdb/ipa_kdb.h            |  8 +++++
 daemons/ipa-kdb/ipa_kdb_mspac.c      | 60 ++++++++++++++++++++++++++++++++++++
 daemons/ipa-kdb/ipa_kdb_principals.c | 55 +++++++++++++++++++++++++++++++++
 3 files changed, 123 insertions(+)

diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
index 4abb733..a6f4481 100644
--- a/daemons/ipa-kdb/ipa_kdb.h
+++ b/daemons/ipa-kdb/ipa_kdb.h
@@ -274,6 +274,14 @@ krb5_error_code ipadb_check_transited_realms(krb5_context 
kcontext,
                                             const krb5_data *tr_contents,
                                             const krb5_data *client_realm,
                                             const krb5_data *server_realm);
+/* Checks whether a principal's realm is one of trusted domains' realm or 
NetBIOS name
+ * and returns the realm of the matched trusted domain in 'trusted_domain'
+ * Returns 0 in case of success and KRB5_KDB_NOENTRY otherwise
+ * If DAL driver is not initialized, returns KRB5_KDB_DBNOTINITED */
+krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
+                                                  const char *test_realm, 
size_t size,
+                                                  char **trusted_realm);
+
 /* DELEGATION CHECKS */
 
 krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
index 3c0dca8..8594309 100644
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
@@ -2790,3 +2790,63 @@ krb5_error_code 
ipadb_check_transited_realms(krb5_context kcontext,
        }
        return ret;
 }
+
+/* Checks whether a principal's realm is one of trusted domains' realm or 
NetBIOS name
+ * and returns the realm of the matched trusted domain in 'trusted_domain'
+ * Returns 0 in case of success and KRB5_KDB_NOENTRY otherwise
+ * If DAL driver is not initialized, returns KRB5_KDB_DBNOTINITED */
+krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
+                                                 const char *test_realm, 
size_t size,
+                                                 char **trusted_realm)
+{
+       struct ipadb_context *ipactx;
+       int i, j, length;
+       const char *name;
+
+       if (test_realm == NULL || test_realm[0] == '\0') {
+               return KRB5_KDB_NOENTRY;
+       }
+
+       ipactx = ipadb_get_context(kcontext);
+       if (!ipactx || !ipactx->mspac) {
+               return KRB5_KDB_DBNOTINITED;
+       }
+
+       /* First, compare realm with ours, it would not be from a trusted realm 
then */
+       if (strncasecmp(test_realm, ipactx->realm, size) == 0) {
+               return KRB5_KDB_NOENTRY;
+       }
+
+       if (!ipactx->mspac || !ipactx->mspac->trusts) {
+               return KRB5_KDB_NOENTRY;
+       }
+
+       /* Iterate through list of trusts and check if input realm belongs to 
any of the trust */
+       for(i = 0 ; i < ipactx->mspac->num_trusts ; i++) {
+               if ((strncasecmp(test_realm,
+                                ipactx->mspac->trusts[i].domain_name,
+                                size) == 0) ||
+                   (strncasecmp(test_realm,
+                                ipactx->mspac->trusts[i].flat_name,
+                                size) == 0)) {
+                       /* return the realm if caller supplied a place for it */
+                       if (trusted_realm != NULL) {
+                               name = (ipactx->mspac->trusts[i].parent_name != 
NULL) ?
+                                       ipactx->mspac->trusts[i].parent_name :
+                                       ipactx->mspac->trusts[i].domain_name;
+                               length = strlen(name) + 1;
+                               *trusted_realm = calloc(1, length);
+                               if (*trusted_realm != NULL) {
+                                       for (j = 0; j < length; j++) {
+                                               (*trusted_realm)[j] = 
toupper(name[j]);
+                                       }
+                               } else {
+                                       return KRB5_KDB_NOENTRY;
+                               }
+                       }
+                       return 0;
+               }
+       }
+
+       return KRB5_KDB_NOENTRY;
+}
diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c 
b/daemons/ipa-kdb/ipa_kdb_principals.c
index b3f8b1a..f2a5a41 100644
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
@@ -1023,8 +1023,10 @@ krb5_error_code ipadb_get_principal(krb5_context 
kcontext,
     struct ipadb_context *ipactx;
     krb5_error_code kerr;
     char *principal = NULL;
+    char *trusted_realm = NULL;
     LDAPMessage *res = NULL;
     LDAPMessage *lentry;
+    krb5_db_entry *kentry = NULL;
     uint32_t pol;
 
     ipactx = ipadb_get_context(kcontext);
@@ -1044,6 +1046,55 @@ krb5_error_code ipadb_get_principal(krb5_context 
kcontext,
 
     kerr = ipadb_find_principal(kcontext, flags, res, &principal, &lentry);
     if (kerr != 0) {
+        if ((kerr == KRB5_KDB_NOENTRY) &&
+            ((flags & (KRB5_KDB_FLAG_CANONICALIZE |
+                       KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY)) != 0)) {
+
+            /* First check if we got enterprise principal which looks like
+             * username\@enterprise_realm@REALM */
+            char *realm;
+            krb5_data *upn;
+
+            upn = krb5_princ_component(kcontext, search_for,
+                                       krb5_princ_size(kcontext, search_for) - 
1);
+
+            if (upn == NULL) {
+                kerr = KRB5_KDB_NOENTRY;
+                goto done;
+            }
+
+            realm = memrchr(upn->data, '@', upn->length);
+            if (realm == NULL) {
+                kerr = KRB5_KDB_NOENTRY;
+                goto done;
+            }
+
+            /* skip '@' and use part after '@' as an enterprise realm for 
comparison */
+            realm++;
+
+            kerr = ipadb_is_princ_from_trusted_realm(kcontext,
+                                                     realm,
+                                                     upn->length - (realm - 
upn->data),
+                                                     &trusted_realm);
+            if (kerr == 0) {
+                kentry = calloc(1, sizeof(krb5_db_entry));
+                if (!kentry) {
+                    kerr = ENOMEM;
+                    goto done;
+                }
+                kerr = krb5_parse_name(kcontext, principal,
+                                       &kentry->princ);
+                if (kerr != 0) {
+                    goto done;
+                }
+
+                kerr = krb5_set_principal_realm(kcontext, kentry->princ, 
trusted_realm);
+                if (kerr != 0) {
+                    goto done;
+                }
+                *entry = kentry;
+            }
+        }
         goto done;
     }
 
@@ -1060,6 +1111,10 @@ krb5_error_code ipadb_get_principal(krb5_context 
kcontext,
     }
 
 done:
+    free(trusted_realm);
+    if ((kerr != 0) && (kentry != NULL)) {
+        ipadb_free_principal(kcontext, kentry);
+    }
     ldap_msgfree(res);
     krb5_free_unparsed_name(kcontext, principal);
     return kerr;
-- 
2.4.3

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to