[Freeipa-devel] [PATCH] (master) Support case-insensitive searches for principals during TGS request processing

Alexander Bokovoy abokovoy at redhat.com
Thu Mar 29 13:30:35 UTC 2012


Hi,

Attached patch implements solution for https://fedorahosted.org/freeipa/ticket/1577.

With the patch applied to master, FreeIPA will be more forgiving for service
principals requested with wrong character case. This is something
supported by Active Directory where principals for services are
case-insensitive and therefore HTTP/ or http/ point to the same service.

In Kerberos LDAP schema, unfortunately, both krbPrincipalName and
krbCanonicalName attributes are defined with exact match strategy,
therefore, making impossible case-insensitive filtering. The patch
solves this problem by introducing an attribute with case-insensitive
matching strategy. Since there are several ways to create principals, we
hook up at both FreeIPA management framework and KDC database plugin
levels to intercept and maintain principal changes.

We have decided only support case-insensitive searches for services
where this is important and required feature. Services always have
krbPrincipalName defined, and ipaKrbPrincipal object class will now be
applied in addition to existing ones, to provide ipaKrbPrincipalAlias
attribute (which is case-insensitive). Users will not get
case-insensitive searches by default, though it is possible to introduce
such feature to them as well by adding ipaKrbPrincipal class to the user
and taking care of creating/maintaining the alias attribute synchronized
with krbPrincipalName. Simo was originally against making
case-insensitive aliases for users, though.

Here is how it will look and act for services:
------------------------------------------------------------------
[root at m17 ~]# kvno cifs/M17.ipa.Local at IPA.LOCAL
cifs/M17.ipa.Local at IPA.LOCAL: kvno = 1
[root at m17 ~]# kvno CIFS/M17.ipa.Local at IPA.LOCAL
CIFS/M17.ipa.Local at IPA.LOCAL: kvno = 1
[root at m17 ~]# klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: admin at IPA.LOCAL

Valid starting     Expires            Service principal
03/29/12 14:38:57  03/30/12 14:38:55  krbtgt/IPA.LOCAL at IPA.LOCAL
03/29/12 14:39:05  03/30/12 14:38:55  HTTP/m17.ipa.local at IPA.LOCAL
03/29/12 15:00:51  03/30/12 14:38:55  ldap/m17.ipa.local at IPA.LOCAL
03/29/12 15:08:02  03/30/12 14:38:55  krbtgt/IPA.LocAl at IPA.LOCAL
03/29/12 15:08:19  03/30/12 14:38:55  cifs/M17.ipa.Local at IPA.LOCAL
03/29/12 15:08:48  03/30/12 14:38:55  CIFS/M17.ipa.Local at IPA.LOCAL
------------------------------------------------------------------

Please note that case-insensitive realm search is still not supported,
even if you enable DNS resolution of realms and KDCs:

[root at m17 ~]# kvno cIfS/m17.ipa.local at ipa.LoCaL
kvno: Cannot find KDC for requested realm while getting credentials for cIfS/m17.ipa.local at ipa.LoCaL

This is due to some krbtgt/realm at REALM searches performed in KDC without
allowing for principal aliases and therefore no chance to our
case-insensitive searches to kick in. Additional discussion is needed, I
think, if we want to support case-insensitive realms.

-- 
/ Alexander Bokovoy
-------------- next part --------------
>From 7d0c8669df0da44c9056581bd29af3f0abec58d4 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Mon, 26 Mar 2012 14:23:42 +0300
Subject: [PATCH 6/9] Perform case-insensitive searches for principals on TGS
 requests

We want to always resolve TGS requests even if the user mistakenly sends a
request for a service ticket where the fqdn part contain upper case letters.

The actual implementation follows hints set by KDC. When AP_REQ is done, KDC
sets KRB5_FLAG_ALIAS_OK and we obey it when looking for principals on TGS requests.

https://fedorahosted.org/freeipa/ticket/1577
---
 daemons/ipa-kdb/ipa_kdb_principals.c |   73 ++++++++++++++++++++++++----------
 install/share/61kerberos-ipav3.ldif  |    3 ++
 install/share/Makefile.am            |    1 +
 install/updates/10-60basev3.update   |    2 +
 ipalib/plugins/service.py            |    7 +++-
 ipaserver/install/dsinstance.py      |    1 +
 6 files changed, 65 insertions(+), 22 deletions(-)
 create mode 100644 install/share/61kerberos-ipav3.ldif

diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
index 1432619..2e190a1 100644
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
@@ -22,6 +22,16 @@
 
 #include "ipa_kdb.h"
 
+/*
+ * During TGS request search by ipaKrbPrincipalName (case-insensitive)
+ * and krbPrincipalName (case-sensitive)
+ */
+#define PRINC_TGS_SEARCH_FILTER "(&(|(objectclass=krbprincipalaux)" \
+                                    "(objectclass=krbprincipal)" \
+                                    "(objectclass=ipakrbprincipal))" \
+                                    "(|(ipakrbprincipalalias=%s)" \
+                                      "(krbprincipalname=%s)))"
+
 #define PRINC_SEARCH_FILTER "(&(|(objectclass=krbprincipalaux)" \
                                 "(objectclass=krbprincipal))" \
                               "(krbprincipalname=%s))"
@@ -29,6 +39,7 @@
 static char *std_principal_attrs[] = {
     "krbPrincipalName",
     "krbCanonicalName",
+    "ipaKrbPrincipalAlias",
     "krbUPEnabled",
     "krbPrincipalKey",
     "krbTicketPolicyReference",
@@ -73,6 +84,7 @@ static char *std_principal_obj_classes[] = {
     "krbprincipal",
     "krbprincipalaux",
     "krbTicketPolicyAux",
+    "ipakrbprincipal",
 
     NULL
 };
@@ -636,13 +648,14 @@ done:
 }
 
 static krb5_error_code ipadb_fetch_principals(struct ipadb_context *ipactx,
-                                              char *search_expr,
+                                              unsigned int flags,
+                                              char *principal,
                                               LDAPMessage **result)
 {
     krb5_error_code kerr;
     char *src_filter = NULL;
-    char *esc_search_expr = NULL;
-    int ret;
+    char *esc_original_princ = NULL;
+    int ret, i;
 
     if (!ipactx->lcontext) {
         ret = ipadb_get_connection(ipactx);
@@ -654,13 +667,19 @@ static krb5_error_code ipadb_fetch_principals(struct ipadb_context *ipactx,
 
     /* escape filter but do not touch '*' as this function accepts
      * wildcards in names */
-    esc_search_expr = ipadb_filter_escape(search_expr, false);
-    if (!esc_search_expr) {
+    esc_original_princ = ipadb_filter_escape(principal, false);
+    if (!esc_original_princ) {
         kerr = KRB5_KDB_INTERNAL_ERROR;
         goto done;
     }
 
-    ret = asprintf(&src_filter, PRINC_SEARCH_FILTER, esc_search_expr);
+    if (flags & KRB5_KDB_FLAG_ALIAS_OK) {
+        ret = asprintf(&src_filter, PRINC_TGS_SEARCH_FILTER,
+                       esc_original_princ, esc_original_princ);
+    } else {
+        ret = asprintf(&src_filter, PRINC_SEARCH_FILTER, esc_original_princ);
+    }
+
     if (ret == -1) {
         kerr = KRB5_KDB_INTERNAL_ERROR;
         goto done;
@@ -673,7 +692,7 @@ static krb5_error_code ipadb_fetch_principals(struct ipadb_context *ipactx,
 
 done:
     free(src_filter);
-    free(esc_search_expr);
+    free(esc_original_princ);
     return kerr;
 }
 
@@ -713,9 +732,12 @@ static krb5_error_code ipadb_find_principal(krb5_context kcontext,
         /* we need to check for a strict match as a '*' in the name may have
          * caused the ldap server to return multiple entries */
         for (i = 0; vals[i]; i++) {
-            /* FIXME: use case insensitive compare and tree as alias ?? */
-            if (strcmp(vals[i]->bv_val, (*principal)) == 0) {
-                found = true;
+            /* KDC will accept aliases when doing TGT lookup (ref_tgt_again in do_tgs_req.c */
+            /* Use case-insensitive comparison in such cases */
+            if ((flags & KRB5_KDB_FLAG_ALIAS_OK) != 0) {
+                found = (strcasecmp(vals[i]->bv_val, (*principal)) == 0);
+            } else {
+                found = (strcmp(vals[i]->bv_val, (*principal)) == 0);
             }
         }
 
@@ -731,11 +753,15 @@ static krb5_error_code ipadb_find_principal(krb5_context kcontext,
             continue;
         }
 
-        /* FIXME: use case insensitive compare and treat as alias ?? */
-        if (strcmp(vals[0]->bv_val, (*principal)) != 0 &&
-                !(flags & KRB5_KDB_FLAG_ALIAS_OK)) {
+        /* Again, if aliases are accepted by KDC, use case-insensitive comparison */
+        if ((flags & KRB5_KDB_FLAG_ALIAS_OK) != 0) {
+            found = (strcasecmp(vals[0]->bv_val, (*principal)) == 0);
+        } else {
+            found = (strcmp(vals[0]->bv_val, (*principal)) == 0);
+        }
+
+        if (!found) {
             /* search does not allow aliases */
-            found = false;
             ldap_value_free_len(vals);
             continue;
         }
@@ -883,7 +909,7 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext,
         goto done;
     }
 
-    kerr = ipadb_fetch_principals(ipactx, principal, &res);
+    kerr = ipadb_fetch_principals(ipactx, flags, principal, &res);
     if (kerr != 0) {
         goto done;
     }
@@ -1398,6 +1424,11 @@ static krb5_error_code ipadb_entry_to_mods(krb5_context kcontext,
         if (kerr) {
             goto done;
         }
+        kerr = ipadb_get_ldap_mod_str(imods, "ipaKrbPrincipalAlias",
+                                      principal, mod_op);
+        if (kerr) {
+            goto done;
+        }
     }
 
     /* KADM5_PRINC_EXPIRE_TIME */
@@ -1735,13 +1766,13 @@ static krb5_error_code ipadb_add_principal(krb5_context kcontext,
         goto done;
     }
 
-    kerr = ipadb_entry_to_mods(kcontext, imods,
-                               entry, principal, LDAP_MOD_ADD);
+    kerr = ipadb_entry_default_attrs(imods);
     if (kerr != 0) {
         goto done;
     }
 
-    kerr = ipadb_entry_default_attrs(imods);
+    kerr = ipadb_entry_to_mods(kcontext, imods,
+                               entry, principal, LDAP_MOD_ADD);
     if (kerr != 0) {
         goto done;
     }
@@ -1779,7 +1810,7 @@ static krb5_error_code ipadb_modify_principal(krb5_context kcontext,
             goto done;
         }
 
-        kerr = ipadb_fetch_principals(ipactx, principal, &res);
+        kerr = ipadb_fetch_principals(ipactx, 0, principal, &res);
         if (kerr != 0) {
             goto done;
         }
@@ -1930,7 +1961,7 @@ krb5_error_code ipadb_delete_principal(krb5_context kcontext,
         goto done;
     }
 
-    kerr = ipadb_fetch_principals(ipactx, principal, &res);
+    kerr = ipadb_fetch_principals(ipactx, 0, principal, &res);
     if (kerr != 0) {
         goto done;
     }
@@ -1982,7 +2013,7 @@ krb5_error_code ipadb_iterate(krb5_context kcontext,
     }
 
     /* fetch list of principal matching filter */
-    kerr = ipadb_fetch_principals(ipactx, match_entry, &res);
+    kerr = ipadb_fetch_principals(ipactx, 0, match_entry, &res);
     if (kerr != 0) {
         goto done;
     }
diff --git a/install/share/61kerberos-ipav3.ldif b/install/share/61kerberos-ipav3.ldif
new file mode 100644
index 0000000..dcdaa5d
--- /dev/null
+++ b/install/share/61kerberos-ipav3.ldif
@@ -0,0 +1,3 @@
+dn: cn=schema
+attributeTypes: (2.16.840.1.113730.3.8.11.32 NAME 'ipaKrbPrincipalAlias' DESC 'IPA principal alias' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3')
+objectClasses: (2.16.840.1.113730.3.8.12.8 NAME 'ipaKrbPrincipal' SUP krbPrincipalAux AUXILIARY MUST ( krbPrincipalName $ ipaKrbPrincipalAlias ) X-ORIGIN 'IPA v3' )
diff --git a/install/share/Makefile.am b/install/share/Makefile.am
index 81fd0dc..68c98e0 100644
--- a/install/share/Makefile.am
+++ b/install/share/Makefile.am
@@ -9,6 +9,7 @@ app_DATA =				\
 	60basev2.ldif			\
 	60basev3.ldif			\
 	60ipadns.ldif			\
+	61kerberos-ipav3.ldif		\
 	65ipasudo.ldif			\
 	anonymous-vlv.ldif		\
 	bootstrap-template.ldif		\
diff --git a/install/updates/10-60basev3.update b/install/updates/10-60basev3.update
index 796eb16..96d012c 100644
--- a/install/updates/10-60basev3.update
+++ b/install/updates/10-60basev3.update
@@ -4,3 +4,5 @@ add:attributeTypes: ( 2.16.840.1.113730.3.8.11.21 NAME 'ipaAllowToImpersonate' D
 add:attributeTypes: ( 2.16.840.1.113730.3.8.11.22 NAME 'ipaAllowedTarget' DESC 'Target principals alowed to get a ticket for' SUP distinguishedName X-ORIGIN 'IPA-v3')
 add:objectClasses: (2.16.840.1.113730.3.8.12.6 NAME 'groupOfPrincipals' SUP top AUXILIARY MUST ( cn ) MAY ( memberPrincipal ) X-ORIGIN 'IPA v3' )
 add:objectClasses: (2.16.840.1.113730.3.8.12.7 NAME 'ipaKrb5DelegationACL' SUP groupOfPrincipals STRUCTURAL MAY ( ipaAllowToImpersonate $$ ipaAllowedTarget ) X-ORIGIN 'IPA v3' )
+add:attributeTypes: (2.16.840.1.113730.3.8.11.32 NAME 'ipaKrbPrincipalAlias' DESC 'IPA principal alias' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3')
+add:objectClasses: (2.16.840.1.113730.3.8.12.8 NAME 'ipaKrbPrincipal' SUP krbPrincipalAux AUXILIARY MUST ( krbPrincipalName $$ ipaKrbPrincipalAlias ) X-ORIGIN 'IPA v3' )
diff --git a/ipalib/plugins/service.py b/ipalib/plugins/service.py
index 7c563b3..d2ddd4e 100644
--- a/ipalib/plugins/service.py
+++ b/ipalib/plugins/service.py
@@ -221,7 +221,7 @@ class service(LDAPObject):
     object_name_plural = _('services')
     object_class = [
         'krbprincipal', 'krbprincipalaux', 'krbticketpolicyaux', 'ipaobject',
-        'ipaservice', 'pkiuser'
+        'ipaservice', 'pkiuser', 'ipakrbprincipal'
     ]
     search_attributes = ['krbprincipalname', 'managedby']
     default_attributes = ['krbprincipalname', 'usercertificate', 'managedby']
@@ -293,6 +293,11 @@ class service_add(LDAPCreate):
         if not 'managedby' in entry_attrs:
              entry_attrs['managedby'] = hostresult['dn']
 
+        # Enforce ipaKrbPrincipalAlias to aid case-insensitive searches
+        # as krbPrincipalName/krbCanonicalName are case-sensitive in Kerberos
+        # schema
+        entry_attrs['ipakrbprincipalalias'] = keys[-1]
+
         return dn
 
 api.register(service_add)
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index e549e13..5d40941 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -359,6 +359,7 @@ class DsInstance(service.Service):
                              "60basev2.ldif",
                              "60basev3.ldif",
                              "60ipadns.ldif",
+                             "61kerberos-ipav3.ldif",
                              "65ipasudo.ldif"):
             target_fname = schema_dirname(self.serverid) + schema_fname
             shutil.copyfile(ipautil.SHARE_DIR + schema_fname, target_fname)
-- 
1.7.9.3



More information about the Freeipa-devel mailing list