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

Sumit Bose sbose at redhat.com
Fri Dec 3 10:12:58 UTC 2010


On Wed, Dec 01, 2010 at 03:12:02PM -0500, Stephen Gallagher wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> On 11/18/2010 08:15 AM, Sumit Bose wrote:
> > 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.
> > 
> 
> Patch 0001: Ack.
> Patch 0002: This patch won't apply. Is it dependent on another patch
> currently in review? I tried atop master and atop the nine patches for
> TGT renewal, but it is still missing blobs according to git.

This need to be applied on top of the ticket renewal patches, because
both change krb5_child. Maybe some changes in master causes this issue,
I have attached rebased versions of the patches.

bye,
Sumit

> 
> 
> - -- 
> Stephen Gallagher
> RHCE 804006346421761
> 
> Delivering value year after year.
> Red Hat ranks #1 in value among software vendors.
> http://www.redhat.com/promo/vendor/
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.11 (GNU/Linux)
> Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/
> 
> iEYEARECAAYFAkz2rBIACgkQeiVVYja6o6N88wCeNp3UlaYT8V4bOYVWWo8L66SZ
> hMcAoKtG8H5uz2SnIrI3M97dloN5MuyL
> =+JsY
> -----END PGP SIGNATURE-----
> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://fedorahosted.org/mailman/listinfo/sssd-devel
-------------- next part --------------
From e535e7727780b4e229e92afac27509e8cb197726 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 c12478f..8611595 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;
@@ -992,7 +1008,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 0d61a31dc2aff6b2497f8f247d79dcf667578996 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          |  307 +++++++++++++++++++++++++++++-
 src/providers/krb5/krb5_common.c         |   22 ++-
 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, 450 insertions(+), 20 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 95a130a..18148bc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -520,6 +520,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 d1341d9..5219086 100644
--- a/src/man/sssd-krb5.5.xml
+++ b/src/man/sssd-krb5.5.xml
@@ -358,6 +358,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 786be83..9eaa011 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 c172eac..f6769ee 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -289,7 +289,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 515c181..e6b680e 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -646,14 +646,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 8611595..a3793ca 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);
@@ -958,7 +1069,7 @@ static errno_t renew_tgt_child(int fd, struct krb5_req *kr)
         DEBUG(9, ("TGT validation is disabled.\n"));
     }
 
-    if (kr->validate) {
+    if (kr->validate || kr->fast_ccname != NULL) {
         /* We drop root privileges which were needed to read the keytab file
          * for the validation of the credentials or for FAST here to run the
          * ccache I/O operations with user privileges. */
@@ -1126,10 +1237,158 @@ 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;
     char *lifetime_str;
+    char *use_fast_str;
     krb5_deltat lifetime;
 
     kr->krb5_ctx = talloc_zero(kr, struct krb5_child_ctx);
@@ -1239,6 +1498,46 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
         krb5_get_init_creds_opt_set_tkt_life(kr->options, lifetime);
     }
 
+    use_fast_str = getenv(SSSD_KRB5_USE_FAST);
+    if (use_fast_str == NULL || strcasecmp(use_fast_str, "never") == 0) {
+        DEBUG(9, ("Not using FAST.\n"));
+    } else if (strcasecmp(use_fast_str, "try") == 0 ||
+               strcasecmp(use_fast_str, "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(use_fast_str, "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 43535c1..ea77237 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -43,7 +43,8 @@ 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_lifetime(struct dp_option *opts, const int opt_id,
@@ -100,11 +101,13 @@ done:
 
 
 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;
     const char *dummy;
+    char *use_fast_str;
 
     realm = dp_opt_get_cstring(opts, KRB5_REALM);
     if (realm == NULL) {
@@ -138,6 +141,21 @@ errno_t check_and_export_options(struct dp_option *opts,
         return ret;
     }
 
+
+    use_fast_str = dp_opt_get_string(opts, KRB5_USE_FAST);
+    if (use_fast_str != NULL) {
+        ret = check_fast(use_fast_str, &krb5_ctx->use_fast);
+        if (ret != EOK) {
+            DEBUG(1, ("check_fast failed.\n"));
+            return ret;
+        }
+
+        ret = setenv(SSSD_KRB5_USE_FAST, 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