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@m17 ~]# kvno cifs/M17.ipa.Local@IPA.LOCAL
cifs/M17.ipa.Local@IPA.LOCAL: kvno = 1
[root@m17 ~]# kvno CIFS/M17.ipa.Local@IPA.LOCAL
CIFS/M17.ipa.Local@IPA.LOCAL: kvno = 1
[root@m17 ~]# klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: admin@IPA.LOCAL

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

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

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

This is due to some krbtgt/realm@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
>From 7d0c8669df0da44c9056581bd29af3f0abec58d4 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <aboko...@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

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to