[SSSD] [PATCHES] Add support for FAST in krb5 provider
Sumit Bose
sbose at redhat.com
Tue Dec 7 09:30:45 UTC 2010
On Mon, Dec 06, 2010 at 09:18:56AM -0500, Stephen Gallagher wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> On 12/06/2010 09:07 AM, Sumit Bose wrote:
> > On Mon, Dec 06, 2010 at 08:38:54AM -0500, Stephen Gallagher wrote:
> > On 12/03/2010 03:56 PM, Sumit Bose wrote:
> >>>>> Although it might be recommended it is not necessary that the keytab
> >>>>> entry for FAST or the TGT validation comes from the same REALM, only the
> >>>>> KDC needs to know how the create tickets for the principal. One use case
> >>>>> e.g. would be a setup with AD and IPA where the user principal coming
> >>>>> from the AD realm and the host entry in the keytab from the IPA realm
> >>>>> and both realm trust each other.
> >>>>
> >
> > I don't understand still why just taking the last entry in the keytab
> > makes sense. If we have a contrived situation like the one you describe,
> > wouldn't it be more sensible for us to add an option to specify the
> > principal in the keytab that we're looking for, rather than hoping that
> > the last one in the list happens to be correct?
> >
> >> I think this is a good idea. Would you mind opening a ticket to add
> >> krb5_validate_principal and krb5_fast_principal options?
>
>
> Ok, I will do that. However, please modify the current patch so that we
> just fail if we don't find the principal we expect.
ok, done. I also fixed an issue with offline authentication. New patches
attached.
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/
>
> iEYEARECAAYFAkz88NAACgkQeiVVYja6o6Nz5wCfUAbBogbJppZqcXNH1PrY+YJ/
> 6EYAoKqI6fWuhWsWHBZpNpe3dx2XKBk2
> =T5kT
> -----END PGP SIGNATURE-----
> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://fedorahosted.org/mailman/listinfo/sssd-devel
-------------- next part --------------
From 39a0bf63f92e23ac34e95a8c7a61ad0a38b07597 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 bf94c7d360e9478c9a79d27f57f410ea5e733575 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 | 313 +++++++++++++++++++++++++++++-
src/providers/krb5/krb5_common.c | 24 ++-
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, 458 insertions(+), 20 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index 6a12fe4..e354de9 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 9584de1..18d8e9d 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 8da83ee..35583af 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -146,7 +146,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 451a153..4d79c03 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..44853e7 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,85 @@ 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;
+ bool principal_found = false;
+
+ 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 (krb5_princ_realm(ctx, entry.principal)->length == strlen(realm) &&
+ 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"));
+ principal_found = true;
+ 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;
+ }
+
+ if (!principal_found) {
+ kerr = KRB5_KT_NOTFOUND;
+ DEBUG(1, ("No principal from realm [%s] found in keytab.\n", realm));
+ 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 +664,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 +729,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 +1078,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 +1246,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;
+ char *ccname;
+ char *server_name;
+ 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;
+ }
+
+ 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 +1502,48 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
krb5_get_init_creds_opt_set_tkt_life(kr->options, lifetime);
}
+ if (!offline) {
+ 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..25188c5 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,23 @@ 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;
+ }
+
+ if (krb5_ctx->use_fast) {
+ 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