[SSSD] [PATCHES] Add support for FAST in krb5 provider

Sumit Bose sbose at redhat.com
Thu Nov 18 13:15:42 UTC 2010


Hi,

these two patches add support for FAST to the Kerberos provider as
requested in trac ticket #237. The first patch just modifies some helper
functions in krb5_child so that they can be used by the second patch.

We support FAST only with MIT Kerberos 1.8 or later, because with 1.7 it
is not possilbe to implement the 'try' option anthentication would fail
if the server does not support FAST.

bye,
Sumit
-------------- next part --------------
From 4d221d5029120db0584654fb1ca717a27f3a18d8 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 18 Nov 2010 12:41:18 +0100
Subject: [PATCH 1/2] Refactor krb5_child to make helpers more flexible

---
 src/providers/krb5/krb5_child.c |   56 +++++++++++++++++++++++++--------------
 1 files changed, 36 insertions(+), 20 deletions(-)

diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 5267605..46b10d7 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -171,7 +171,8 @@ static krb5_error_code sss_krb5_prompter(krb5_context context, void *data,
 }
 
 
-static krb5_error_code create_empty_cred(struct krb5_req *kr, krb5_creds **_cred)
+static krb5_error_code create_empty_cred(krb5_context ctx, krb5_principal princ,
+                                         krb5_creds **_cred)
 {
     krb5_error_code kerr;
     krb5_creds *cred = NULL;
@@ -183,15 +184,15 @@ static krb5_error_code create_empty_cred(struct krb5_req *kr, krb5_creds **_cred
         return ENOMEM;
     }
 
-    kerr = krb5_copy_principal(kr->ctx, kr->princ, &cred->client);
+    kerr = krb5_copy_principal(ctx, princ, &cred->client);
     if (kerr != 0) {
         DEBUG(1, ("krb5_copy_principal failed.\n"));
         goto done;
     }
 
-    krb5_realm = krb5_princ_realm(kr->ctx, kr->princ);
+    krb5_realm = krb5_princ_realm(ctx, princ);
 
-    kerr = krb5_build_principal_ext(kr->ctx, &cred->server,
+    kerr = krb5_build_principal_ext(ctx, &cred->server,
                                     krb5_realm->length, krb5_realm->data,
                                     KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
                                     krb5_realm->length, krb5_realm->data, 0);
@@ -203,7 +204,7 @@ static krb5_error_code create_empty_cred(struct krb5_req *kr, krb5_creds **_cred
 done:
     if (kerr != 0) {
         if (cred != NULL && cred->client != NULL) {
-            krb5_free_principal(kr->ctx, cred->client);
+            krb5_free_principal(ctx, cred->client);
         }
 
         free(cred);
@@ -214,7 +215,9 @@ done:
     return kerr;
 }
 
-static krb5_error_code create_ccache_file(struct krb5_req *kr, krb5_creds *creds)
+static krb5_error_code create_ccache_file(krb5_context ctx,
+                                          krb5_principal princ,
+                                          char *ccname, krb5_creds *creds)
 {
     krb5_error_code kerr;
     krb5_ccache tmp_cc = NULL;
@@ -224,11 +227,12 @@ static krb5_error_code create_ccache_file(struct krb5_req *kr, krb5_creds *creds
     char *dummy;
     char *tmp_ccname;
     krb5_creds *l_cred;
+    TALLOC_CTX *tmp_ctx = NULL;
 
-    if (strncmp(kr->ccname, "FILE:", 5) == 0) {
-        cc_file_name = kr->ccname + 5;
+    if (strncmp(ccname, "FILE:", 5) == 0) {
+        cc_file_name = ccname + 5;
     } else {
-        cc_file_name = kr->ccname;
+        cc_file_name = ccname;
     }
 
     if (cc_file_name[0] != '/') {
@@ -236,27 +240,36 @@ static krb5_error_code create_ccache_file(struct krb5_req *kr, krb5_creds *creds
         return EINVAL;
     }
 
+    tmp_ctx = talloc_new(tmp_ctx);
+    if (tmp_ctx == NULL) {
+        DEBUG(1, ("talloc_new failed.\n"));
+        return ENOMEM;
+    }
+
     dummy = strrchr(cc_file_name, '/');
-    tmp_ccname = talloc_strndup(kr, cc_file_name, (size_t) (dummy-cc_file_name));
+    tmp_ccname = talloc_strndup(tmp_ctx, cc_file_name,
+                                (size_t) (dummy-cc_file_name));
     if (tmp_ccname == NULL) {
         DEBUG(1, ("talloc_strdup failed.\n"));
-        return ENOMEM;
+        kerr = ENOMEM;
+        goto done;
     }
     tmp_ccname = talloc_asprintf_append(tmp_ccname, "/.krb5cc_dummy_XXXXXX");
 
     fd = mkstemp(tmp_ccname);
     if (fd == -1) {
         DEBUG(1, ("mkstemp failed [%d][%s].\n", errno, strerror(errno)));
-        return errno;
+        kerr = errno;
+        goto done;
     }
 
-    kerr = krb5_cc_resolve(kr->ctx, tmp_ccname, &tmp_cc);
+    kerr = krb5_cc_resolve(ctx, tmp_ccname, &tmp_cc);
     if (kerr != 0) {
         KRB5_DEBUG(1, kerr);
         goto done;
     }
 
-    kerr = krb5_cc_initialize(kr->ctx, tmp_cc, kr->princ);
+    kerr = krb5_cc_initialize(ctx, tmp_cc, princ);
     if (kerr != 0) {
         KRB5_DEBUG(1, kerr);
         goto done;
@@ -267,7 +280,7 @@ static krb5_error_code create_ccache_file(struct krb5_req *kr, krb5_creds *creds
     }
 
     if (creds == NULL) {
-        kerr = create_empty_cred(kr, &l_cred);
+        kerr = create_empty_cred(ctx, princ, &l_cred);
         if (kerr != 0) {
             KRB5_DEBUG(1, kerr);
             goto done;
@@ -276,13 +289,13 @@ static krb5_error_code create_ccache_file(struct krb5_req *kr, krb5_creds *creds
         l_cred = creds;
     }
 
-    kerr = krb5_cc_store_cred(kr->ctx, tmp_cc, l_cred);
+    kerr = krb5_cc_store_cred(ctx, tmp_cc, l_cred);
     if (kerr != 0) {
         KRB5_DEBUG(1, kerr);
         goto done;
     }
 
-    kerr = krb5_cc_close(kr->ctx, tmp_cc);
+    kerr = krb5_cc_close(ctx, tmp_cc);
     if (kerr != 0) {
         KRB5_DEBUG(1, kerr);
         goto done;
@@ -309,8 +322,11 @@ done:
         close(fd);
     }
     if (kerr != 0 && tmp_cc != NULL) {
-        krb5_cc_destroy(kr->ctx, tmp_cc);
+        krb5_cc_destroy(ctx, tmp_cc);
     }
+
+    talloc_free(tmp_ctx);
+
     return kerr;
 }
 
@@ -608,7 +624,7 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
         DEBUG(9, ("TGT validation is disabled.\n"));
     }
 
-    kerr = create_ccache_file(kr, kr->creds);
+    kerr = create_ccache_file(kr->ctx, kr->princ, kr->ccname, kr->creds);
     if (kerr != 0) {
         KRB5_DEBUG(1, kerr);
         goto done;
@@ -970,7 +986,7 @@ static errno_t create_empty_ccache(int fd, struct krb5_req *kr)
     int ret;
     int pam_status = PAM_SUCCESS;
 
-    ret = create_ccache_file(kr, NULL);
+    ret = create_ccache_file(kr->ctx, kr->princ, kr->ccname, NULL);
     if (ret != 0) {
         KRB5_DEBUG(1, ret);
         pam_status = PAM_SYSTEM_ERR;
-- 
1.7.3.2

-------------- next part --------------
From da966de0a52718b02e5d46ae2854538bfa3c76e9 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 18 Nov 2010 12:48:05 +0100
Subject: [PATCH 2/2] Add support for FAST in krb5 provider

---
 Makefile.am                              |    1 +
 src/config/SSSDConfig.py                 |    1 +
 src/config/SSSDConfigTest.py             |    9 +-
 src/config/etc/sssd.api.d/sssd-krb5.conf |    1 +
 src/external/krb5.m4                     |    4 +-
 src/man/sssd-krb5.5.xml                  |   35 ++++
 src/providers/ipa/ipa_common.c           |    3 +-
 src/providers/ipa/ipa_common.h           |    2 +-
 src/providers/ipa/ipa_init.c             |    3 +-
 src/providers/krb5/krb5_auth.c           |   12 +-
 src/providers/krb5/krb5_child.c          |  304 +++++++++++++++++++++++++++++-
 src/providers/krb5/krb5_common.c         |   21 ++-
 src/providers/krb5/krb5_common.h         |    6 +-
 src/providers/krb5/krb5_init.c           |    2 +-
 src/util/sss_krb5.c                      |   48 +++++
 src/util/sss_krb5.h                      |   14 ++
 16 files changed, 447 insertions(+), 19 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 4acf3b4..8bfbb4a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -519,6 +519,7 @@ krb5_utils_tests_SOURCES = \
     src/tests/krb5_utils-tests.c \
     src/providers/krb5/krb5_utils.c \
     src/providers/krb5/krb5_common.c \
+    src/util/sss_krb5.c \
     src/providers/data_provider_fo.c \
     src/providers/data_provider_opts.c \
     src/providers/data_provider_callbacks.c \
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index edfc823..7da1027 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -107,6 +107,7 @@ option_strings = {
     'krb5_renewable_lifetime' : _("Renewable lifetime of the TGT"),
     'krb5_lifetime' : _("Lifetime of the TGT"),
     'krb5_renew_interval' : _("Time between two checks for renewal"),
+    'krb5_use_fast' : _("Enables FAST"),
 
     # [provider/krb5/chpass]
     'krb5_kpasswd' : _('Server where the change password service is running if not on the KDC'),
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index b51614d..8991f36 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -552,7 +552,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
              'krb5_auth_timeout',
              'krb5_renewable_lifetime',
              'krb5_lifetime',
-             'krb5_renew_interval'])
+             'krb5_renew_interval',
+             'krb5_use_fast'])
 
         options = domain.list_options()
 
@@ -729,7 +730,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'krb5_auth_timeout',
             'krb5_renewable_lifetime',
             'krb5_lifetime',
-            'krb5_renew_interval']
+            'krb5_renew_interval',
+            'krb5_use_fast']
 
         self.assertTrue(type(options) == dict,
                         "Options should be a dictionary")
@@ -880,7 +882,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
              'krb5_auth_timeout',
              'krb5_renewable_lifetime',
              'krb5_lifetime',
-             'krb5_renew_interval'])
+             'krb5_renew_interval',
+             'krb5_use_fast'])
 
         options = domain.list_options()
 
diff --git a/src/config/etc/sssd.api.d/sssd-krb5.conf b/src/config/etc/sssd.api.d/sssd-krb5.conf
index 86eff20..bb3a077 100644
--- a/src/config/etc/sssd.api.d/sssd-krb5.conf
+++ b/src/config/etc/sssd.api.d/sssd-krb5.conf
@@ -14,6 +14,7 @@ krb5_store_password_if_offline = bool, None, false
 krb5_renewable_lifetime = str, None, false
 krb5_lifetime = str, None, false
 krb5_renew_interval = int, None, false
+krb5_use_fast = str, None, false
 
 [provider/krb5/chpass]
 
diff --git a/src/external/krb5.m4 b/src/external/krb5.m4
index d4f563f..d248cd7 100644
--- a/src/external/krb5.m4
+++ b/src/external/krb5.m4
@@ -39,7 +39,9 @@ LIBS="$LIBS $KRB5_LIBS"
 AC_CHECK_HEADERS([krb5.h krb5/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_expire_callback \
+                krb5_get_init_creds_opt_set_fast_ccache_name \
+                krb5_get_init_creds_opt_set_fast_flags])
 CFLAGS=$SAVE_CFLAGS
 LIBS=$SAVE_LIBS
 
diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml
index 34a1c72..c5b3fe3 100644
--- a/src/man/sssd-krb5.5.xml
+++ b/src/man/sssd-krb5.5.xml
@@ -344,6 +344,41 @@
                     </listitem>
                 </varlistentry>
 
+                <varlistentry>
+                    <term>krb5_use_fast (string)</term>
+                    <listitem>
+                        <para>
+                            Enables flexible authentication secure tunneling
+                            (FAST) for Kerberos pre-authentication. The
+                            following options are supported:
+                        </para>
+                        <para>
+                            <emphasis>never</emphasis> use FAST, this is
+                            equivalent to not set this option at all.
+                        </para>
+                        <para>
+                            <emphasis>try</emphasis> to use FAST, if the server
+                            does not support fast continue without.
+                        </para>
+                        <para>
+                            <emphasis>demand</emphasis> to use FAST, fail if the
+                            server does not require fast.
+                        </para>
+                        <para>
+                            Default: not set, i.e. FAST is not used.
+                        </para>
+                        <para>
+                            Please note that a keytab is required to use fast.
+                        </para>
+                        <para>
+                            Please note also that sssd supports fast only with
+                            MIT Kerberos version 1.8 and above. If sssd used
+                            used with an older version using this option is a
+                            configuration error.
+                        </para>
+                    </listitem>
+                </varlistentry>
+
             </variablelist>
         </para>
     </refsect1>
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 72c6be0..439ada0 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -142,7 +142,8 @@ struct dp_option ipa_def_krb5_opts[] = {
     { "krb5_store_password_if_offline", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "krb5_renewable_lifetime", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "krb5_lifetime", DP_OPT_STRING, NULL_STRING, NULL_STRING },
-    { "krb5_renew_interval", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER }
+    { "krb5_renew_interval", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER },
+    { "krb5_use_fast", DP_OPT_STRING, NULL_STRING, NULL_STRING }
 };
 
 int ipa_get_options(TALLOC_CTX *memctx,
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index f0d4ad8..ba8f85e 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -40,7 +40,7 @@ struct ipa_service {
 /* the following define is used to keep track of the options in the krb5
  * module, so that if they change and ipa is not updated correspondingly
  * this will trigger a runtime abort error */
-#define IPA_KRB5_OPTS_TEST 12
+#define IPA_KRB5_OPTS_TEST 13
 
 enum ipa_basic_opt {
     IPA_DOMAIN = 0,
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index 3eaa0a1..2a63ac8 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -288,7 +288,8 @@ int sssm_ipa_auth_init(struct be_ctx *bectx,
         }
     }
 
-    ret = check_and_export_options(krb5_auth_ctx->opts, bectx->domain);
+    ret = check_and_export_options(krb5_auth_ctx->opts, bectx->domain,
+                                   krb5_auth_ctx);
     if (ret != EOK) {
         DEBUG(1, ("check_and_export_opts failed.\n"));
         goto done;
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index d895b8e..4a3bc8a 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -643,14 +643,16 @@ static void krb5_find_ccache_step(struct tevent_req *req)
     }
 
     /* We need to keep the root privileges to read the keytab file if
-     * validation is enabled, otherwise we can drop them and run krb5_child
-     * with user privileges.
+     * validation or FAST is enabled, otherwise we can drop them and run
+     * krb5_child with user privileges.
      * If we are offline we want to create an empty ccache file. In this
      * case we can drop the privileges, too. */
-    if (!dp_opt_get_bool(kr->krb5_ctx->opts, KRB5_VALIDATE) || kr->is_offline) {
-        kr->run_as_user = true;
-    } else {
+    if ((dp_opt_get_bool(kr->krb5_ctx->opts, KRB5_VALIDATE) ||
+         kr->krb5_ctx->use_fast) &&
+        !kr->is_offline) {
         kr->run_as_user = false;
+    } else {
+        kr->run_as_user = true;
     }
 
     subreq = handle_child_send(state, state->ev, kr);
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 46b10d7..adbdc98 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -90,6 +90,7 @@ struct krb5_req {
     char *ccname;
     char *keytab;
     bool validate;
+    char *fast_ccname;
 
     const char *upn;
     uid_t uid;
@@ -491,6 +492,76 @@ static errno_t add_ticket_times_to_response(struct krb5_req *kr)
     return ret;
 }
 
+static krb5_error_code find_principal_in_keytab(krb5_context ctx,
+                                                krb5_keytab keytab,
+                                                const char *realm,
+                                                krb5_principal *princ)
+{
+    krb5_error_code kerr;
+    krb5_error_code kt_err;
+    krb5_error_code kerr_d;
+    krb5_kt_cursor cursor;
+    krb5_keytab_entry entry;
+
+    memset(&cursor, 0, sizeof(cursor));
+    kerr = krb5_kt_start_seq_get(ctx, keytab, &cursor);
+    if (kerr != 0) {
+        DEBUG(1, ("krb5_kt_start_seq_get failed.\n"));
+        KRB5_DEBUG(1, kerr);
+        return kerr;
+    }
+
+    /* We look for the first entry from our realm or take the last one */
+    memset(&entry, 0, sizeof(entry));
+    while ((kt_err = krb5_kt_next_entry(ctx, keytab, &entry, &cursor)) == 0) {
+        if (strncmp(krb5_princ_realm(ctx, entry.principal)->data, realm,
+                    krb5_princ_realm(ctx, entry.principal)->length) == 0) {
+            DEBUG(9, ("Found keytab entry with the realm of the credential.\n"));
+            break;
+        }
+
+        kerr = krb5_free_keytab_entry_contents(ctx, &entry);
+        if (kerr != 0) {
+            DEBUG(1, ("Failed to free keytab entry.\n"));
+        }
+        memset(&entry, 0, sizeof(entry));
+    }
+
+    /* Close the keytab here.  Even though we're using cursors, the file
+     * handle is stored in the krb5_keytab structure, and it gets
+     * overwritten by other keytab calls, creating a leak. */
+    kerr = krb5_kt_end_seq_get(ctx, keytab, &cursor);
+    if (kerr != 0) {
+        DEBUG(1, ("krb5_kt_end_seq_get failed.\n"));
+        goto done;
+    }
+
+    /* check if we got any errors from krb5_kt_next_entry */
+    if (kt_err != 0 && kt_err != KRB5_KT_END) {
+        DEBUG(1, ("Error while reading keytab.\n"));
+        KRB5_DEBUG(1, kerr);
+        goto done;
+    }
+
+    kerr = krb5_copy_principal(ctx, entry.principal, princ);
+    if (kerr != 0) {
+        DEBUG(1, ("krb5_copy_principal failed.\n"));
+        KRB5_DEBUG(1, kerr);
+        goto done;
+    }
+
+    kerr = 0;
+
+done:
+    kerr_d = krb5_free_keytab_entry_contents(ctx, &entry);
+    if (kerr_d != 0) {
+        DEBUG(1, ("Failed to free keytab entry.\n"));
+        KRB5_DEBUG(1, kerr_d);
+    }
+
+    return kerr;
+}
+
 static krb5_error_code validate_tgt(struct krb5_req *kr)
 {
     krb5_error_code kerr;
@@ -584,6 +655,43 @@ done:
 
 }
 
+static krb5_error_code get_and_save_tgt_with_keytab(krb5_context ctx,
+                                                    krb5_principal princ,
+                                                    krb5_keytab keytab,
+                                                    char *ccname)
+{
+    krb5_error_code kerr = 0;
+    krb5_creds creds;
+    krb5_get_init_creds_opt options;
+
+    memset(&creds, 0, sizeof(creds));
+    memset(&options, 0, sizeof(options));
+
+    krb5_get_init_creds_opt_set_address_list(&options, NULL);
+    krb5_get_init_creds_opt_set_forwardable(&options, 0);
+    krb5_get_init_creds_opt_set_proxiable(&options, 0);
+
+    kerr = krb5_get_init_creds_keytab(ctx, &creds, princ, keytab, 0, NULL,
+                                      &options);
+    if (kerr != 0) {
+        KRB5_DEBUG(1, kerr);
+        return kerr;
+    }
+
+    kerr = create_ccache_file(ctx, princ, ccname, &creds);
+    if (kerr != 0) {
+        KRB5_DEBUG(1, kerr);
+        goto done;
+    }
+    kerr = 0;
+
+done:
+    krb5_free_cred_contents(ctx, &creds);
+
+    return kerr;
+
+}
+
 static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
                                         char *password)
 {
@@ -612,16 +720,19 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
             return kerr;
         }
 
+    } else {
+        DEBUG(9, ("TGT validation is disabled.\n"));
+    }
+
+    if (kr->validate || kr->fast_ccname != NULL) {
         /* We drop root privileges which were needed to read the keytab file
-         * for the validation validation of the credentials here to run the
+         * for the validation of the credentials or for FAST here to run the
          * ccache I/O operations with user privileges. */
         ret = become_user(kr->uid, kr->gid);
         if (ret != EOK) {
             DEBUG(1, ("become_user failed.\n"));
             return ret;
         }
-    } else {
-        DEBUG(9, ("TGT validation is disabled.\n"));
     }
 
     kerr = create_ccache_file(kr->ctx, kr->princ, kr->ccname, kr->creds);
@@ -1104,6 +1215,153 @@ static int krb5_cleanup(void *ptr)
     return EOK;
 }
 
+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)
+{
+    krb5_error_code krberr;
+    krb5_ccache ccache = NULL;
+    krb5_creds mcred;
+    krb5_creds cred;
+
+    krberr = krb5_cc_resolve(ctx, ccname, &ccache);
+    if (krberr != 0) {
+        DEBUG(1, ("krb5_cc_resolve failed.\n"));
+        goto done;
+    }
+
+    memset(&mcred, 0, sizeof(mcred));
+    memset(&cred, 0, sizeof(mcred));
+
+    mcred.server = server_principal;
+    mcred.client = client_principal;
+
+    krberr = krb5_cc_retrieve_cred(ctx, ccache, 0, &mcred, &cred);
+    if (krberr != 0) {
+        DEBUG(1, ("krb5_cc_retrieve_cred failed.\n"));
+        krberr = 0;
+        goto done;
+    }
+
+    tgtt->authtime = cred.times.authtime;
+    tgtt->starttime = cred.times.starttime;
+    tgtt->endtime = cred.times.endtime;
+    tgtt->renew_till = cred.times.renew_till;
+
+    krb5_free_cred_contents(ctx, &cred);
+
+    krberr = 0;
+
+done:
+    if (ccache != NULL) {
+        krb5_cc_close(ctx, ccache);
+    }
+
+    return krberr;
+}
+
+static krb5_error_code check_fast_ccache(krb5_context ctx, const char *realm,
+                                         const char *keytab_name,
+                                         TALLOC_CTX *mem_ctx,
+                                         char **fast_ccname)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    krb5_error_code kerr;
+    int ret;
+    char *ccname;
+    char *server_name;
+    struct stat statbuf;
+    krb5_ticket_times tgtt;
+    krb5_keytab keytab = NULL;
+    krb5_principal client_princ = NULL;
+    krb5_principal server_princ = NULL;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(1, ("talloc_new failed.\n"));
+        kerr = ENOMEM;
+        goto done;
+    }
+
+    ccname = talloc_asprintf(tmp_ctx, "FILE:%s/fast_ccache_%s", DB_PATH, realm);
+    if (ccname == NULL) {
+        DEBUG(1, ("talloc_asprintf failed.\n"));
+        kerr = ENOMEM;
+        goto done;
+    }
+
+    if (keytab_name != NULL) {
+        kerr = krb5_kt_resolve(ctx, keytab_name, &keytab);
+    } else {
+        kerr = krb5_kt_default(ctx, &keytab);
+    }
+    if (kerr) {
+        DEBUG(0, ("Failed to read keytab file [%s].\n",
+                  keytab_name != NULL ? keytab_name : "(default)"));
+        goto done;
+    }
+
+    kerr = find_principal_in_keytab(ctx, keytab, realm, &client_princ);
+    if (kerr != 0) {
+        DEBUG(1, ("find_principal_in_keytab failed.\n"));
+        goto done;
+    }
+
+    ret = stat(ccname+5, &statbuf);
+    if (ret == EOK) {
+        server_name = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", realm, realm);
+        if (server_name == NULL) {
+            DEBUG(1, ("talloc_asprintf failed.\n"));
+            kerr = ENOMEM;
+            goto done;
+        }
+
+        kerr = krb5_parse_name(ctx, server_name, &server_princ);
+        if (kerr != 0) {
+            DEBUG(1, ("krb5_parse_name failed.\n"));
+            goto done;
+        }
+
+        memset(&tgtt, 0, sizeof(tgtt));
+        kerr = get_tgt_times(ctx, ccname, server_princ, client_princ, &tgtt);
+        if (kerr == 0) {
+            if (tgtt.endtime > time(NULL)) {
+                DEBUG(5, ("FAST TGT is still valid.\n"));
+                goto done;
+            }
+        }
+    }
+
+    kerr = get_and_save_tgt_with_keytab(ctx, client_princ, keytab, ccname);
+    if (kerr != 0) {
+        DEBUG(1, ("get_and_save_tgt_with_keytab failed.\n"));
+        goto done;
+    }
+
+
+    kerr = 0;
+
+done:
+    if (client_princ != NULL) {
+        krb5_free_principal(ctx, client_princ);
+    }
+    if (server_princ != NULL) {
+        krb5_free_principal(ctx, server_princ);
+    }
+
+    if (kerr == 0) {
+        *fast_ccname = talloc_steal(mem_ctx, ccname);
+    }
+    talloc_free(tmp_ctx);
+
+    if (keytab != NULL) {
+        krb5_kt_close(ctx, keytab);
+    }
+
+    return kerr;
+}
+
 static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
 {
     krb5_error_code kerr = 0;
@@ -1215,6 +1473,46 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
         krb5_get_init_creds_opt_set_tkt_life(kr->options, lifetime);
     }
 
+    dummy = getenv(SSSD_KRB5_USE_FAST);
+    if (dummy == NULL || strcasecmp(dummy, "never") == 0) {
+        DEBUG(9, ("Not using FAST.\n"));
+    } else if (strcasecmp(dummy, "try") == 0 ||
+               strcasecmp(dummy, "demand") == 0) {
+        kerr = check_fast_ccache(kr->ctx, kr->krb5_ctx->realm, kr->keytab,
+                                 kr, &kr->fast_ccname);
+        if (kerr != 0) {
+            DEBUG(1, ("check_fast_ccache failed.\n"));
+            KRB5_DEBUG(1, kerr);
+            goto failed;
+        }
+
+        kerr = sss_krb5_get_init_creds_opt_set_fast_ccache_name(kr->ctx,
+                                                                kr->options,
+                                                                kr->fast_ccname);
+        if (kerr != 0) {
+            DEBUG(1, ("sss_krb5_get_init_creds_opt_set_fast_ccache_name "
+                      "failed.\n"));
+            KRB5_DEBUG(1, kerr);
+            goto failed;
+        }
+
+        if (strcasecmp(dummy, "demand") == 0) {
+            kerr = sss_krb5_get_init_creds_opt_set_fast_flags(kr->ctx,
+                                                              kr->options,
+                                                              KRB5_FAST_REQUIRED);
+            if (kerr != 0) {
+                DEBUG(1, ("sss_krb5_get_init_creds_opt_set_fast_flags "
+                          "failed.\n"));
+                KRB5_DEBUG(1, kerr);
+                goto failed;
+            }
+        }
+    } else {
+        DEBUG(1, ("Unsupported value [%s] for krb5_use_fast.\n"));
+        kerr = EINVAL;
+        goto failed;
+    }
+
 /* TODO: set options, e.g.
  *  krb5_get_init_creds_opt_set_forwardable
  *  krb5_get_init_creds_opt_set_proxiable
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 0a4ebbf..c93fad3 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -43,11 +43,13 @@ struct dp_option default_krb5_opts[] = {
     { "krb5_store_password_if_offline", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "krb5_renewable_lifetime", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "krb5_lifetime", DP_OPT_STRING, NULL_STRING, NULL_STRING },
-    { "krb5_renew_interval", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER }
+    { "krb5_renew_interval", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER },
+    { "krb5_use_fast", DP_OPT_STRING, NULL_STRING, NULL_STRING }
 };
 
 errno_t check_and_export_options(struct dp_option *opts,
-                                 struct sss_domain_info *dom)
+                                 struct sss_domain_info *dom,
+                                 struct krb5_ctx *krb5_ctx)
 {
     int ret;
     const char *realm;
@@ -109,6 +111,21 @@ errno_t check_and_export_options(struct dp_option *opts,
         }
     }
 
+
+    str = dp_opt_get_string(opts, KRB5_USE_FAST);
+    if (str != NULL) {
+        ret = check_fast(str, &krb5_ctx->use_fast);
+        if (ret != EOK) {
+            DEBUG(1, ("check_fast failed.\n"));
+            return ret;
+        }
+
+        ret = setenv(SSSD_KRB5_USE_FAST, str, 1);
+        if (ret != EOK) {
+            DEBUG(2, ("setenv [%s] failed.\n", SSSD_KRB5_USE_FAST));
+        }
+    }
+
     dummy = dp_opt_get_cstring(opts, KRB5_KDC);
     if (dummy == NULL) {
         DEBUG(1, ("No KDC explicitly configured, using defaults.\n"));
diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h
index c20c7b3..9ca0146 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -37,6 +37,7 @@
 #define SSSD_KRB5_REALM "SSSD_KRB5_REALM"
 #define SSSD_KRB5_RENEWABLE_LIFETIME "SSSD_KRB5_RENEWABLE_LIFETIME"
 #define SSSD_KRB5_LIFETIME "SSSD_KRB5_LIFETIME"
+#define SSSD_KRB5_USE_FAST "SSSD_KRB5_USE_FAST"
 
 #define KDCINFO_TMPL PUBCONF_PATH"/kdcinfo.%s"
 #define KPASSWDINFO_TMPL PUBCONF_PATH"/kpasswdinfo.%s"
@@ -57,6 +58,7 @@ enum krb5_opts {
     KRB5_RENEWABLE_LIFETIME,
     KRB5_LIFETIME,
     KRB5_RENEW_INTERVAL,
+    KRB5_USE_FAST,
 
     KRB5_OPTS
 };
@@ -114,6 +116,7 @@ struct krb5_ctx {
 
     struct deferred_auth_ctx *deferred_auth_ctx;
     struct renew_tgt_ctx *renew_tgt_ctx;
+    bool use_fast;
 };
 
 struct remove_info_files_ctx {
@@ -124,7 +127,8 @@ struct remove_info_files_ctx {
 };
 
 errno_t check_and_export_options(struct dp_option *opts,
-                                 struct sss_domain_info *dom);
+                                 struct sss_domain_info *dom,
+                                 struct krb5_ctx *krb5_ctx);
 
 errno_t krb5_try_kdcip(TALLOC_CTX *memctx, struct confdb_ctx *cdb,
                        const char *conf_path, struct dp_option *opts);
diff --git a/src/providers/krb5/krb5_init.c b/src/providers/krb5/krb5_init.c
index 0f1ed41..525ba4d 100644
--- a/src/providers/krb5/krb5_init.c
+++ b/src/providers/krb5/krb5_init.c
@@ -138,7 +138,7 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
         }
     }
 
-    ret = check_and_export_options(ctx->opts, bectx->domain);
+    ret = check_and_export_options(ctx->opts, bectx->domain, ctx);
     if (ret != EOK) {
         DEBUG(1, ("check_and_export_options failed.\n"));
         goto fail;
diff --git a/src/util/sss_krb5.c b/src/util/sss_krb5.c
index 7fad32e..174b6d9 100644
--- a/src/util/sss_krb5.c
+++ b/src/util/sss_krb5.c
@@ -361,3 +361,51 @@ krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_set_expire_callback(
     return 0;
 #endif
 }
+
+errno_t check_fast(const char *str, bool *use_fast)
+{
+#if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_FLAGS
+    if (strcasecmp(str, "never") == 0 ) {
+        *use_fast = false;
+    } else if (strcasecmp(str, "try") == 0 || strcasecmp(str, "demand") == 0) {
+        *use_fast = true;
+    } else {
+        sss_log(SSS_LOG_ALERT, "Unsupported value [%s] for option krb5_use_fast,"
+                               "please use never, try, or demand.\n");
+        return EINVAL;
+    }
+
+    return EOK;
+#else
+    sss_log(SSS_LOG_ALERT, "This build of sssd done not support FAST. "
+                           "Please remove option krb5_use_fast.\n");
+    return EINVAL;
+#endif
+}
+
+krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_set_fast_ccache_name(
+                                                   krb5_context context,
+                                                   krb5_get_init_creds_opt *opt,
+                                                   const char *fast_ccache_name)
+{
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_CCACHE_NAME
+    return krb5_get_init_creds_opt_set_fast_ccache_name(context, opt,
+                                                        fast_ccache_name);
+#else
+    DEBUG(5, ("krb5_get_init_creds_opt_set_fast_ccache_name not available.\n"));
+    return 0;
+#endif
+}
+
+krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_set_fast_flags(
+                                                   krb5_context context,
+                                                   krb5_get_init_creds_opt *opt,
+                                                   krb5_flags flags)
+{
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_FLAGS
+    return krb5_get_init_creds_opt_set_fast_flags(context, opt, flags);
+#else
+    DEBUG(5, ("krb5_get_init_creds_opt_set_fast_flags not available.\n"));
+    return 0;
+#endif
+}
diff --git a/src/util/sss_krb5.h b/src/util/sss_krb5.h
index cf6feda..d74df83 100644
--- a/src/util/sss_krb5.h
+++ b/src/util/sss_krb5.h
@@ -32,6 +32,8 @@
 #include <krb5.h>
 #endif
 
+#include "util/util.h"
+
 const char * KRB5_CALLCONV sss_krb5_get_error_message (krb5_context,
                                                    krb5_error_code);
 
@@ -67,4 +69,16 @@ krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_set_expire_callback(
                                                    krb5_get_init_creds_opt *opt,
                                                    krb5_expire_callback_func cb,
                                                    void *data);
+
+errno_t check_fast(const char *str, bool *use_fast);
+
+krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_set_fast_ccache_name(
+                                                  krb5_context context,
+                                                  krb5_get_init_creds_opt *opt,
+                                                  const char *fast_ccache_name);
+
+krb5_error_code KRB5_CALLCONV sss_krb5_get_init_creds_opt_set_fast_flags(
+                                                   krb5_context context,
+                                                   krb5_get_init_creds_opt *opt,
+                                                   krb5_flags flags);
 #endif /* __SSS_KRB5_H__ */
-- 
1.7.3.2



More information about the sssd-devel mailing list