From 51803b0e91393cdb1e2eadc9a31de64f55b3b0e6 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Tue, 20 Dec 2011 16:13:59 -0500
Subject: [PATCH] Add compatibility layer for Heimdal Kerberos implementation

---
 src/external/krb5.m4            |   10 +++++-
 src/providers/krb5/krb5_child.c |   10 +++--
 src/providers/krb5/krb5_utils.c |   10 +++--
 src/providers/ldap/ldap_child.c |    5 +++
 src/util/sss_krb5.c             |   70 ++++++++++++++++++++++++++++++++-------
 src/util/sss_krb5.h             |   15 ++++++++
 6 files changed, 99 insertions(+), 21 deletions(-)

diff --git a/src/external/krb5.m4 b/src/external/krb5.m4
index 40d632ce595e206197b7fd9dc86e4377fd5938e8..a8707c6e1bd94b2456fe55c25a4776c1c781b5bc 100644
--- a/src/external/krb5.m4
+++ b/src/external/krb5.m4
@@ -37,13 +37,21 @@ SAVE_LIBS=$LIBS
 CFLAGS="$CFLAGS $KRB5_CFLAGS"
 LIBS="$LIBS $KRB5_LIBS"
 AC_CHECK_HEADERS([krb5.h krb5/krb5.h])
+AC_CHECK_TYPES([krb5_ticket_times, krb5_times], [], [],
+               [[#include <krb5.h>]])
 AC_CHECK_FUNCS([krb5_get_init_creds_opt_alloc krb5_get_error_message \
                 krb5_free_unparsed_name \
                 krb5_get_init_creds_opt_set_expire_callback \
                 krb5_get_init_creds_opt_set_fast_ccache_name \
                 krb5_get_init_creds_opt_set_fast_flags \
                 krb5_get_init_creds_opt_set_canonicalize \
-                krb5_unparse_name_flags])
+                krb5_unparse_name_flags \
+                krb5_get_init_creds_opt_set_change_password_prompt \
+                krb5_free_keytab_entry_contents \
+                krb5_kt_free_entry \
+                krb5_princ_realm \
+                krb5_get_time_offsets \
+                krb5_principal_get_realm])
 CFLAGS=$SAVE_CFLAGS
 LIBS=$SAVE_LIBS
 
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 297e3a763fe45265290d21a1c508c125685a3d4e..c83179b4b1cc1e6eb8578fe7491c01743ead0b39 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -530,7 +530,7 @@ static krb5_error_code validate_tgt(struct krb5_req *kr)
             break;
         }
 
-        kerr = krb5_free_keytab_entry_contents(kr->ctx, &entry);
+        kerr = sss_krb5_free_keytab_entry_contents(kr->ctx, &entry);
         if (kerr != 0) {
             DEBUG(1, ("Failed to free keytab entry.\n"));
         }
@@ -578,7 +578,7 @@ done:
     if (krb5_kt_close(kr->ctx, keytab) != 0) {
         DEBUG(1, ("krb5_kt_close failed"));
     }
-    if (krb5_free_keytab_entry_contents(kr->ctx, &entry) != 0) {
+    if (sss_krb5_free_keytab_entry_contents(kr->ctx, &entry) != 0) {
         DEBUG(1, ("Failed to free keytab entry.\n"));
     }
     if (principal != NULL) {
@@ -1194,7 +1194,7 @@ static int krb5_cleanup(void *ptr)
 static krb5_error_code get_tgt_times(krb5_context ctx, const char *ccname,
                                      krb5_principal server_principal,
                                      krb5_principal client_principal,
-                                     krb5_ticket_times *tgtt)
+                                     sss_krb5_ticket_times *tgtt)
 {
     krb5_error_code krberr;
     krb5_ccache ccache = NULL;
@@ -1247,7 +1247,7 @@ static krb5_error_code check_fast_ccache(krb5_context ctx, const char *primary,
     krb5_error_code kerr;
     char *ccname;
     char *server_name;
-    krb5_ticket_times tgtt;
+    sss_krb5_ticket_times tgtt;
     krb5_keytab keytab = NULL;
     krb5_principal client_princ = NULL;
     krb5_principal server_princ = NULL;
@@ -1420,6 +1420,7 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
         goto failed;
     }
 
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_CHANGE_PASSWORD_PROMPT
     /* A prompter is used to catch messages about when a password will
      * expired. The library shall not use the prompter to ask for a new password
      * but shall return KRB5KDC_ERR_KEY_EXP. */
@@ -1428,6 +1429,7 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
         KRB5_DEBUG(1, kerr);
         goto failed;
     }
+#endif
 
     lifetime_str = getenv(SSSD_KRB5_RENEWABLE_LIFETIME);
     if (lifetime_str == NULL) {
diff --git a/src/providers/krb5/krb5_utils.c b/src/providers/krb5/krb5_utils.c
index 2957598cce0dc58334eb64807143e9284350ab8a..c97d58e7c7cfb80b3fd34eaf18a5371669871272 100644
--- a/src/providers/krb5/krb5_utils.c
+++ b/src/providers/krb5/krb5_utils.c
@@ -431,6 +431,8 @@ errno_t get_ccache_file_data(const char *ccache_file, const char *client_name,
     char *server_name;
     krb5_creds mcred;
     krb5_creds cred;
+    const char *realm_name;
+    int realm_length;
 
     kerr = krb5_init_context(&ctx);
     if (kerr != 0) {
@@ -444,11 +446,11 @@ errno_t get_ccache_file_data(const char *ccache_file, const char *client_name,
         goto done;
     }
 
+    sss_krb5_princ_realm(ctx, client_princ, &realm_name, &realm_length);
+
     server_name = talloc_asprintf(NULL, "krbtgt/%.*s@%.*s",
-                                  krb5_princ_realm(ctx, client_princ)->length,
-                                  krb5_princ_realm(ctx, client_princ)->data,
-                                  krb5_princ_realm(ctx, client_princ)->length,
-                                  krb5_princ_realm(ctx, client_princ)->data);
+                                  realm_length, realm_name,
+                                  realm_length, realm_name);
     if (server_name == NULL) {
         kerr = KRB5_CC_NOMEM;
         DEBUG(1, ("talloc_asprintf failed.\n"));
diff --git a/src/providers/ldap/ldap_child.c b/src/providers/ldap/ldap_child.c
index 160cc1ce464f4ff7ec1cfd9fc4eb31e2ddf4c5fe..05aadde908c9b5294fef39e8d3bf983b0d02154a 100644
--- a/src/providers/ldap/ldap_child.c
+++ b/src/providers/ldap/ldap_child.c
@@ -287,6 +287,7 @@ static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX *memctx,
         goto done;
     }
 
+#ifdef HAVE_KRB5_GET_TIME_OFFSETS
     krberr = krb5_get_time_offsets(context, &kdc_time_offset, &kdc_time_offset_usec);
     if (krberr) {
         DEBUG(2, ("Failed to get KDC time offset: %s\n",
@@ -297,6 +298,10 @@ static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX *memctx,
             kdc_time_offset++;
         }
     }
+#else
+    /* If we don't have this function, just assume no offset */
+    kdc_time_offset = 0;
+#endif
 
     krberr = 0;
     *ccname_out = ccname;
diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c
index 3311ef542f13aebd2e322be5ba425f4fc1def838..fe76afc525a053ce3b6d4024300f48a605bda88d 100644
--- a/src/util/sss_krb5.c
+++ b/src/util/sss_krb5.c
@@ -44,6 +44,8 @@ errno_t select_principal_from_keytab(TALLOC_CTX *mem_ctx,
     int i = 0;
     errno_t ret;
     char *principal_string;
+    const char *realm_name;
+    int realm_len;
 
     /**
      * Priority of lookup:
@@ -164,9 +166,11 @@ errno_t select_principal_from_keytab(TALLOC_CTX *mem_ctx,
         }
 
         if (_realm) {
+            sss_krb5_princ_realm(krb_ctx, client_princ,
+                                 &realm_name,
+                                 &realm_len);
             *_realm = talloc_asprintf(mem_ctx, "%.*s",
-                                      krb5_princ_realm(ctx, client_princ)->length,
-                                      krb5_princ_realm(ctx, client_princ)->data);
+                                      realm_len, realm_name);
             if (!*_realm) {
                 DEBUG(1, ("talloc_asprintf failed"));
                 if (_principal) talloc_zfree(*_principal);
@@ -322,7 +326,7 @@ int sss_krb5_verify_keytab_ex(const char *principal, const char *keytab_name,
             found = true;
         }
         free(kt_principal);
-        krberr = krb5_free_keytab_entry_contents(context, &entry);
+        krberr = sss_krb5_free_keytab_entry_contents(context, &entry);
         if (krberr) {
             /* This should never happen. The API docs for this function
              * specify only success for this function
@@ -378,18 +382,19 @@ static bool match_principal(krb5_context ctx,
                      const char *pattern_primary,
                      const char *pattern_realm)
 {
-    krb5_data *realm_data;
     char *primary = NULL;
     char *primary_str = NULL;
     int primary_str_len = 0;
     int tmp_len;
     int len_diff;
+    const char *realm_name;
+    int realm_len;
 
     int mode = MODE_NORMAL;
     TALLOC_CTX *tmp_ctx;
     bool ret = false;
 
-    realm_data = krb5_princ_realm(ctx, principal);
+    sss_krb5_princ_realm(ctx, principal, &realm_name, &realm_len);
 
     tmp_ctx = talloc_new(NULL);
     if (!tmp_ctx) {
@@ -425,8 +430,8 @@ static bool match_principal(krb5_context ctx,
         }
     }
 
-    if (!pattern_realm || (realm_data->length == strlen(pattern_realm) &&
-        strncmp(realm_data->data, pattern_realm, realm_data->length) == 0)) {
+    if (!pattern_realm || (realm_len == strlen(pattern_realm) &&
+        strncmp(realm_name, pattern_realm, realm_len) == 0)) {
         DEBUG(7, ("Principal matched to the sample (%s@%s).\n", pattern_primary,
                                                                 pattern_realm));
         ret = true;
@@ -466,7 +471,7 @@ krb5_error_code find_principal_in_keytab(krb5_context ctx,
             break;
         }
 
-        kerr = krb5_free_keytab_entry_contents(ctx, &entry);
+        kerr = sss_krb5_free_keytab_entry_contents(ctx, &entry);
         if (kerr != 0) {
             DEBUG(1, ("Failed to free keytab entry.\n"));
         }
@@ -504,7 +509,7 @@ krb5_error_code find_principal_in_keytab(krb5_context ctx,
     kerr = 0;
 
 done:
-    kerr_d = krb5_free_keytab_entry_contents(ctx, &entry);
+    kerr_d = sss_krb5_free_keytab_entry_contents(ctx, &entry);
     if (kerr_d != 0) {
         DEBUG(1, ("Failed to free keytab entry.\n"));
     }
@@ -917,9 +922,50 @@ cleanup:
 void sss_krb5_get_init_creds_opt_set_canonicalize(krb5_get_init_creds_opt *opts,
                                                   int canonicalize)
 {
-#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_CANONICALIZE
-    return krb5_get_init_creds_opt_set_canonicalize(opts, canonicalize);
+    /* FIXME: The extra check for HAVE_KRB5_TICKET_TIMES is a workaround due to Heimdal
+     * defining krb5_get_init_creds_opt_set_canonicalize() with a different set of
+     * arguments. We should use a better configure check in the future.
+     */
+#if defined(HAVE_KRB5_GET_INIT_CREDS_OPT_SET_CANONICALIZE) && defined(HAVE_KRB5_TICKET_TIMES)
+    krb5_get_init_creds_opt_set_canonicalize(opts, canonicalize);
 #else
-    DEBUG(SSSDBG_OP_FAILURE, ("Kerberos principal canonicalization is not avaliable!\n"));
+    DEBUG(SSSDBG_OP_FAILURE, ("Kerberos principal canonicalization is not available!\n"));
 #endif
 }
+
+#ifdef HAVE_KRB5_PRINCIPAL_GET_REALM
+void sss_krb5_princ_realm(krb5_context context, krb5_const_principal princ,
+                          const char **realm, int *len)
+{
+    *realm = krb5_principal_get_realm(context, princ);
+    *len = strlen(*realm);
+}
+#else
+void sss_krb5_princ_realm(krb5_context context, krb5_const_principal princ,
+                          const char **realm, int *len)
+{
+    const krb5_data *data;
+
+    data = krb5_princ_realm(context, princ);
+    if (data) {
+        *realm = data->data;
+        *len = data->length;
+    }
+}
+#endif
+
+#ifdef HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS
+krb5_error_code
+sss_krb5_free_keytab_entry_contents(krb5_context context,
+                                    krb5_keytab_entry *entry)
+{
+    return krb5_free_keytab_entry_contents(context, entry);
+}
+#else
+krb5_error_code
+sss_krb5_free_keytab_entry_contents(krb5_context context,
+                                    krb5_keytab_entry *entry)
+{
+    return krb5_kt_free_entry(context, entry);
+}
+#endif
diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h
index 039b79af06ac31be4c08b8737d0c794c98eb9a26..52e6c5d48ad5fac01740516c315a43cd9e9ea023 100644
--- a/src/util/sss_krb5.h
+++ b/src/util/sss_krb5.h
@@ -115,4 +115,19 @@ sss_krb5_unparse_name_flags(krb5_context context, krb5_const_principal principal
 void sss_krb5_get_init_creds_opt_set_canonicalize(krb5_get_init_creds_opt *opts,
                                                   int canonicalize);
 
+/* === Compatibility routines for the Heimdal Kerberos implementation === */
+
+void sss_krb5_princ_realm(krb5_context context, krb5_const_principal princ,
+                          const char **realm, int *len);
+
+krb5_error_code
+sss_krb5_free_keytab_entry_contents(krb5_context context,
+                                    krb5_keytab_entry *entry);
+
+#ifdef HAVE_KRB5_TICKET_TIMES
+typedef krb5_ticket_times sss_krb5_ticket_times;
+#elif HAVE_KRB5_TIMES
+typedef krb5_times sss_krb5_ticket_times;
+#endif
+
 #endif /* __SSS_KRB5_H__ */
-- 
1.7.7.4

