[SSSD] [PATCH] Add support for delayed kinit if offline (rebase for master) and offline fix

Sumit Bose sbose at redhat.com
Wed May 26 09:42:58 UTC 2010


Hi,

the first patch is the rebased version of the delayed authentication
patch for 1.2.

The second patch moves the deletion of the old ccache file to the point
where we already have a new ccache file. Currently it is removed before
we access the server and it is lost if the server does not reply. I
needed this patch to get the delayed authentication patch work properly
with the master branch, but have not seen similar problems with 1.2 so
far. But in general it might be needed for 1.2, too.

bye,
Sumit
-------------- next part --------------
From 8f121334bfce8bd1b152266cf955e6c2c3e9ab73 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 19 Apr 2010 11:59:09 +0200
Subject: [PATCH 1/2] Add support for delayed kinit if offline

If the configuration option krb5_store_password_if_offline is set to
true and the backend is offline the plain text user password is stored
and used to request a TGT if the backend becomes online. If available
the Linux kernel key retention service is used.
---
 contrib/sssd.spec.in                               |    2 +
 src/Makefile.am                                    |    4 +
 src/config/SSSDConfig.py                           |    1 +
 src/config/SSSDConfigTest.py                       |    3 +
 src/config/etc/sssd.api.d/sssd-krb5.conf           |    1 +
 src/configure.ac                                   |    1 +
 src/db/sysdb.h                                     |    1 +
 src/db/sysdb_ops.c                                 |   14 +-
 src/external/libkeyutils.m4                        |   11 +
 src/man/sssd-krb5.5.xml                            |   18 +
 src/providers/data_provider.h                      |   11 +
 src/providers/dp_pam_data_util.c                   |   88 +++++
 src/providers/ipa/ipa_common.c                     |    3 +-
 src/providers/ipa/ipa_common.h                     |    2 +-
 src/providers/ipa/ipa_init.c                       |    9 +
 src/providers/krb5/krb5_auth.c                     |   76 +++--
 src/providers/krb5/krb5_auth.h                     |   10 +
 src/providers/krb5/krb5_common.c                   |    3 +-
 src/providers/krb5/krb5_common.h                   |    1 +
 .../krb5/krb5_delayed_online_authentication.c      |  354 ++++++++++++++++++++
 src/providers/krb5/krb5_init.c                     |    8 +
 src/responder/pam/pamsrv_cmd.c                     |    2 +-
 src/tests/sysdb-tests.c                            |    4 +-
 23 files changed, 593 insertions(+), 34 deletions(-)
 create mode 100644 src/external/libkeyutils.m4
 create mode 100644 src/providers/krb5/krb5_delayed_online_authentication.c

diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index bbc1728..29f2c7d 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -30,6 +30,7 @@ Requires: libdhash = %{dhash_version}-%{release}
 Requires: libcollection = %{collection_version}-%{release}
 Requires: libini_config = %{ini_config_version}-%{release}
 Requires: cyrus-sasl-gssapi
+Requires: keyutils-libs
 Requires(post): python
 Requires(preun):  initscripts chkconfig
 Requires(postun): /sbin/service
@@ -75,6 +76,7 @@ BuildRequires: doxygen
 BuildRequires: libselinux-devel
 BuildRequires: libsemanage-devel
 BuildRequires: bind-utils
+BuildRequires: keyutils-libs-devel
 
 %description
 Provides a set of daemons to manage access to remote directories and
diff --git a/src/Makefile.am b/src/Makefile.am
index 73cd408..d093089 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -773,6 +773,7 @@ libsss_krb5_la_SOURCES = \
     providers/child_common.c \
     providers/krb5/krb5_utils.c \
     providers/krb5/krb5_become_user.c \
+    providers/krb5/krb5_delayed_online_authentication.c \
     providers/krb5/krb5_auth.c \
     providers/krb5/krb5_common.c \
     providers/krb5/krb5_init.c \
@@ -782,6 +783,7 @@ libsss_krb5_la_CFLAGS = \
     $(DHASH_CFLAGS)
 libsss_krb5_la_LIBADD = \
     $(DHASH_LIBS) \
+    $(KEYUITLS_LIB) \
     $(KRB5_LIBS)
 libsss_krb5_la_LDFLAGS = \
     -version-info 1:0:0 \
@@ -813,6 +815,7 @@ libsss_ipa_la_SOURCES = \
     util/find_uid.c \
     providers/krb5/krb5_utils.c \
     providers/krb5/krb5_become_user.c \
+    providers/krb5/krb5_delayed_online_authentication.c \
     providers/krb5/krb5_common.c \
     providers/krb5/krb5_auth.c
 libsss_ipa_la_CFLAGS = \
@@ -823,6 +826,7 @@ libsss_ipa_la_CFLAGS = \
 libsss_ipa_la_LIBADD = \
     $(OPENLDAP_LIBS) \
     $(DHASH_LIBS) \
+    $(KEYUTILS_LIBS) \
     $(KRB5_LIBS)
 libsss_ipa_la_LDFLAGS = \
     -version-info 1:0:0 \
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index 9e178f1..6b759d8 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -100,6 +100,7 @@ option_strings = {
     'krb5_ccname_template' : _("Location of the user's credential cache"),
     'krb5_keytab' : _("Location of the keytab to validate credentials"),
     'krb5_validate' : _("Enable credential validation"),
+    'krb5_store_password_if_offline' : _("Store password if offline for later online authentication"),
 
     # [provider/krb5/chpass]
     'krb5_changepw_principal' : _('The principal of the change password service'),
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index 32bb712..61c2f94 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -548,6 +548,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
              'krb5_ccname_template',
              'krb5_keytab',
              'krb5_validate',
+             'krb5_store_password_if_offline',
              'krb5_auth_timeout'])
 
         options = domain.list_options()
@@ -719,6 +720,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'krb5_ccname_template',
             'krb5_keytab',
             'krb5_validate',
+            'krb5_store_password_if_offline',
             'krb5_auth_timeout']
 
         self.assertTrue(type(options) == dict,
@@ -865,6 +867,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
              'krb5_ccname_template',
              'krb5_keytab',
              'krb5_validate',
+             'krb5_store_password_if_offline',
              'krb5_auth_timeout'])
 
         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 a9c8230..eeb8fe1 100644
--- a/src/config/etc/sssd.api.d/sssd-krb5.conf
+++ b/src/config/etc/sssd.api.d/sssd-krb5.conf
@@ -9,6 +9,7 @@ krb5_ccachedir = str, None, false
 krb5_ccname_template = str, None, false
 krb5_keytab = str, None, false
 krb5_validate = bool, None, false
+krb5_store_password_if_offline = bool, None, false
 
 [provider/krb5/chpass]
 krb5_changepw_principal = str, None, false
diff --git a/src/configure.ac b/src/configure.ac
index 4cc5d85..811c2d9 100644
--- a/src/configure.ac
+++ b/src/configure.ac
@@ -95,6 +95,7 @@ m4_include([external/selinux.m4])
 m4_include([external/crypto.m4])
 m4_include([external/nscd.m4])
 m4_include([external/nsupdate.m4])
+m4_include([external/libkeyutils.m4])
 m4_include([util/signal.m4])
 
 PKG_CHECK_MODULES([DBUS],[dbus-1])
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 5b6f218..23560ec 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -471,6 +471,7 @@ int sysdb_cache_auth(TALLOC_CTX *mem_ctx,
                      const uint8_t *authtok,
                      size_t authtok_size,
                      struct confdb_ctx *cdb,
+                     bool just_check,
                      time_t *_expire_date,
                      time_t *_delayed_until);
 
diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
index 00b74c6..7f45431 100644
--- a/src/db/sysdb_ops.c
+++ b/src/db/sysdb_ops.c
@@ -1988,6 +1988,7 @@ int sysdb_cache_auth(TALLOC_CTX *mem_ctx,
                      const uint8_t *authtok,
                      size_t authtok_size,
                      struct confdb_ctx *cdb,
+                     bool just_check,
                      time_t *_expire_date,
                      time_t *_delayed_until)
 {
@@ -2120,6 +2121,11 @@ int sysdb_cache_auth(TALLOC_CTX *mem_ctx,
         DEBUG(4, ("Hashes do match!\n"));
         authentication_successful = true;
 
+        if (just_check) {
+            ret = EOK;
+            goto done;
+        }
+
         ret = sysdb_attrs_add_time_t(update_attrs,
                                      SYSDB_LAST_LOGIN, time(NULL));
         if (ret != EOK) {
@@ -2168,8 +2174,12 @@ int sysdb_cache_auth(TALLOC_CTX *mem_ctx,
     }
 
 done:
-    *_expire_date = expire_date;
-    *_delayed_until = delayed_until;
+    if (_expire_date != NULL) {
+        *_expire_date = expire_date;
+    }
+    if (_delayed_until != NULL) {
+        *_delayed_until = delayed_until;
+    }
     if (password) for (i = 0; password[i]; i++) password[i] = 0;
     if (ret) {
         ldb_transaction_cancel(sysdb->ldb);
diff --git a/src/external/libkeyutils.m4 b/src/external/libkeyutils.m4
new file mode 100644
index 0000000..5753d77
--- /dev/null
+++ b/src/external/libkeyutils.m4
@@ -0,0 +1,11 @@
+AC_SUBST(KEYUTILS_LIBS)
+
+AC_CHECK_HEADERS([keyutils.h],
+                 [AC_CHECK_LIB([keyutils], [add_key],
+                               [AC_DEFINE(USE_KEYRING, 1, [Define if the keyring should be used])
+                                KEYUTILS_LIBS="-lkeyutils"
+                               ],
+                               [AC_MSG_WARN([No usable keyutils library found])]
+                              )],
+                 [AC_MSG_WARN([keyutils header files are not available])]
+)
diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml
index 01f212d..732f05b 100644
--- a/src/man/sssd-krb5.5.xml
+++ b/src/man/sssd-krb5.5.xml
@@ -241,6 +241,24 @@
                     </listitem>
                 </varlistentry>
 
+                <varlistentry>
+                    <term>krb5_store_password_if_offline (boolean)</term>
+                    <listitem>
+                        <para>
+                            Store the password of the user if the provider is
+                            offline and use it to request a TGT when the
+                            provider gets online again.
+                        </para>
+                        <para>
+                            Please note that this feature currently only
+                            available on a Linux plattform.
+                        </para>
+                        <para>
+                            Default: false
+                        </para>
+                    </listitem>
+                </varlistentry>
+
             </variablelist>
         </para>
     </refsect1>
diff --git a/src/providers/data_provider.h b/src/providers/data_provider.h
index 747e6e8..951b47a 100644
--- a/src/providers/data_provider.h
+++ b/src/providers/data_provider.h
@@ -22,10 +22,16 @@
 #ifndef __DATA_PROVIDER_H__
 #define __DATA_PROVIDER_H__
 
+#include "config.h"
+
 #include <stdint.h>
 #include <sys/un.h>
 #include <errno.h>
 #include <stdbool.h>
+#ifdef USE_KEYRING
+#include <sys/types.h>
+#include <keyutils.h>
+#endif
 #include "talloc.h"
 #include "tevent.h"
 #include "ldb.h"
@@ -178,9 +184,14 @@ struct pam_data {
     bool offline_auth;
     bool last_auth_saved;
     int priv;
+#ifdef USE_KEYRING
+    key_serial_t key_serial;
+#endif
 };
 
 /* from dp_auth_util.c */
+errno_t copy_pam_data(TALLOC_CTX *mem_ctx, struct pam_data *old_pd,
+                      struct pam_data **new_pd);
 void pam_print_data(int l, struct pam_data *pd);
 int pam_add_response(struct pam_data *pd,
                      enum response_type type,
diff --git a/src/providers/dp_pam_data_util.c b/src/providers/dp_pam_data_util.c
index 02eb6e9..d709447 100644
--- a/src/providers/dp_pam_data_util.c
+++ b/src/providers/dp_pam_data_util.c
@@ -25,6 +25,26 @@
 #include "providers/data_provider.h"
 
 
+#define PD_STR_COPY(el) do { \
+    if (old_pd->el != NULL) { \
+        pd->el = talloc_strdup(pd, old_pd->el); \
+        if (pd->el == NULL) { \
+            DEBUG(1, ("talloc_strdup failed.\n")); \
+            goto failed; \
+        } \
+    } \
+} while(0);
+
+#define PD_MEM_COPY(el, size) do { \
+    if (old_pd->el != NULL) { \
+        pd->el = talloc_memdup(pd, old_pd->el, (size)); \
+        if (pd->el == NULL) { \
+            DEBUG(1, ("talloc_memdup failed.\n")); \
+            goto failed; \
+        } \
+    } \
+} while(0);
+
 static const char *pamcmd2str(int cmd) {
     switch (cmd) {
     case SSS_PAM_AUTHENTICATE:
@@ -46,6 +66,74 @@ static const char *pamcmd2str(int cmd) {
     }
 }
 
+int pam_data_destructor(void *ptr)
+{
+    struct pam_data *pd = talloc_get_type(ptr, struct pam_data);
+
+    if (pd->authtok_size != 0 && pd->authtok != NULL) {
+        memset(pd->authtok, 0, pd->authtok_size);
+        pd->authtok_size = 0;
+    }
+
+    if (pd->newauthtok_size != 0 && pd->newauthtok != NULL) {
+        memset(pd->newauthtok, 0, pd->newauthtok_size);
+        pd->newauthtok_size = 0;
+    }
+
+    return EOK;
+}
+
+struct pam_data *create_pam_data(TALLOC_CTX *mem_ctx)
+{
+    struct pam_data *pd;
+
+    pd = talloc_zero(mem_ctx, struct pam_data);
+    if (pd == NULL) {
+        DEBUG(1, ("talloc_zero failed.\n"));
+        return NULL;
+    }
+
+    talloc_set_destructor((TALLOC_CTX *) pd, pam_data_destructor);
+
+    return pd;
+}
+
+errno_t copy_pam_data(TALLOC_CTX *mem_ctx, struct pam_data *old_pd,
+                      struct pam_data **new_pd)
+{
+    struct pam_data *pd = NULL;
+
+    pd = create_pam_data(mem_ctx);
+    if (pd == NULL) {
+        DEBUG(1, ("create_pam_data failed.\n"));
+        return ENOMEM;
+    }
+
+    pd->cmd  = old_pd->cmd;
+    pd->authtok_type = old_pd->authtok_type;
+    pd->authtok_size = old_pd->authtok_size;
+    pd->newauthtok_type = old_pd->newauthtok_type;
+    pd->newauthtok_size = old_pd->newauthtok_size;
+
+    PD_STR_COPY(domain);
+    PD_STR_COPY(user);
+    PD_STR_COPY(service);
+    PD_STR_COPY(tty);
+    PD_STR_COPY(ruser);
+    PD_STR_COPY(rhost);
+    PD_MEM_COPY(authtok, old_pd->authtok_size);
+    PD_MEM_COPY(newauthtok, old_pd->newauthtok_size);
+    pd->cli_pid = old_pd->cli_pid;
+
+    *new_pd = pd;
+
+    return EOK;
+
+failed:
+    talloc_free(pd);
+    return ENOMEM;
+}
+
 void pam_print_data(int l, struct pam_data *pd)
 {
     DEBUG(l, ("command: %s\n", pamcmd2str(pd->cmd)));
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 2ef674e..a8b14d3 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -124,7 +124,8 @@ struct dp_option ipa_def_krb5_opts[] = {
     { "krb5_auth_timeout", DP_OPT_NUMBER, { .number = 15 }, NULL_NUMBER },
     { "krb5_keytab", DP_OPT_STRING, { "/etc/krb5.keytab" }, NULL_STRING },
     { "krb5_validate", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
-    { "krb5_kpasswd", DP_OPT_STRING, NULL_STRING, NULL_STRING }
+    { "krb5_kpasswd", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "krb5_store_password_if_offline", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }
 };
 
 int ipa_get_options(TALLOC_CTX *memctx,
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index f59a7d7..9678e0c 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 9
+#define IPA_KRB5_OPTS_TEST 10
 
 enum ipa_basic_opt {
     IPA_DOMAIN = 0,
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index 2f0ccf0..0e72b1f 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -272,6 +272,15 @@ int sssm_ipa_auth_init(struct be_ctx *bectx,
         goto done;
     }
 
+    if (dp_opt_get_bool(krb5_auth_ctx->opts, KRB5_STORE_PASSWORD_IF_OFFLINE)) {
+        ret = init_delayed_online_authentication(krb5_auth_ctx, bectx,
+                                                 bectx->ev);
+        if (ret != EOK) {
+            DEBUG(1, ("init_delayed_online_authentication failed.\n"));
+            goto done;
+        }
+    }
+
     ret = check_and_export_options(krb5_auth_ctx->opts, bectx->domain);
     if (ret != EOK) {
         DEBUG(1, ("check_and_export_opts failed.\n"));
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 48173ba..a7aebe5 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -371,10 +371,10 @@ static errno_t fork_child(struct tevent_req *req, struct tevent_context *ev,
         /* We need to keep the root privileges to read the keytab file if
          * validation is enabled, otherwise we can drop them here and run
          * krb5_child with user privileges.
-         * If authtok_size is zero we are offline and want to create an empty
-         * ccache file. In this case we can drop the privileges, too. */
+         * If we are offline and 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->pd->authtok_size == 0) {
+            kr->is_offline) {
             ret = become_user(kr->uid, kr->gid);
             if (ret != EOK) {
                 DEBUG(1, ("become_user failed.\n"));
@@ -537,6 +537,7 @@ static void krb5_resolve_kpasswd_done(struct tevent_req *subreq);
 static void krb5_find_ccache_step(struct tevent_req *req);
 static void krb5_save_ccname_done(struct tevent_req *req);
 static void krb5_child_done(struct tevent_req *req);
+static void krb5_pam_handler_cache_auth_step(struct tevent_req *req);
 
 struct krb5_auth_state {
     struct tevent_context *ev;
@@ -897,40 +898,31 @@ static void krb5_find_ccache_step(struct tevent_req *req)
         DEBUG(9, ("Preparing for offline operation.\n"));
         kr->is_offline = true;
 
-        if (kr->valid_tgt_present) {
-            DEBUG(9, ("Valid TGT available, nothing to do.\n"));
+        if (kr->valid_tgt_present || kr->active_ccache_present) {
+            DEBUG(9, ("Valid TGT available or "
+                      "ccache file is already in use.\n"));
             msg = talloc_asprintf(pd, "%s=%s", CCACHE_ENV_NAME, kr->ccname);
             if (msg == NULL) {
                 DEBUG(1, ("talloc_asprintf failed.\n"));
-                ret = ENOMEM;
-                goto done;
+            } else {
+                ret = pam_add_response(pd, SSS_PAM_ENV_ITEM, strlen(msg) + 1,
+                                       (uint8_t *) msg);
+                if (ret != EOK) {
+                    DEBUG(1, ("pam_add_response failed.\n"));
+                }
             }
 
-            ret = pam_add_response(pd, SSS_PAM_ENV_ITEM, strlen(msg) + 1,
-                                   (uint8_t *) msg);
-            if (ret != EOK) {
-                DEBUG(1, ("pam_add_response failed.\n"));
+            if (dp_opt_get_bool(kr->krb5_ctx->opts,
+                                KRB5_STORE_PASSWORD_IF_OFFLINE)) {
+                krb5_pam_handler_cache_auth_step(req);
+                return;
             }
 
             state->pam_status = PAM_AUTHINFO_UNAVAIL;
             state->dp_err = DP_ERR_OFFLINE;
             ret = EOK;
             goto done;
-        }
-        memset(pd->authtok, 0, pd->authtok_size);
-        pd->authtok_size = 0;
 
-        if (kr->active_ccache_present) {
-            ret = krb5_save_ccname(state, state->be_ctx->sysdb,
-                                   state->be_ctx->domain, pd->user,
-                                   kr->ccname);
-            if (ret) {
-                DEBUG(1, ("krb5_save_ccname failed.\n"));
-                goto done;
-            }
-
-            krb5_save_ccname_done(req);
-            return;
         }
     }
 
@@ -1214,6 +1206,11 @@ static void krb5_save_ccname_done(struct tevent_req *req)
     char *password = NULL;
 
     if (kr->is_offline) {
+        if (dp_opt_get_bool(kr->krb5_ctx->opts,KRB5_STORE_PASSWORD_IF_OFFLINE)) {
+            krb5_pam_handler_cache_auth_step(req);
+            return;
+        }
+
         DEBUG(4, ("Backend is marked offline, retry later!\n"));
         state->pam_status = PAM_AUTHINFO_UNAVAIL;
         state->dp_err = DP_ERR_OFFLINE;
@@ -1277,6 +1274,34 @@ done:
 
 }
 
+static void krb5_pam_handler_cache_auth_step(struct tevent_req *req)
+{
+    struct krb5_auth_state *state = tevent_req_data(req, struct krb5_auth_state);
+    struct pam_data *pd = state->pd;
+    struct krb5_ctx *krb5_ctx = state->kr->krb5_ctx;
+    int ret;
+
+    ret = sysdb_cache_auth(state, state->be_ctx->sysdb, state->be_ctx->domain,
+                           pd->user, pd->authtok, pd->authtok_size,
+                           state->be_ctx->cdb, true, NULL, NULL);
+    if (ret != EOK) {
+        DEBUG(1, ("Offline authentication failed\n"));
+        state->pam_status = PAM_SYSTEM_ERR;
+        state->dp_err = DP_ERR_OK;
+    } else {
+        ret = add_user_to_delayed_online_authentication(krb5_ctx, pd,
+                                                       state->kr->uid);
+        if (ret != EOK) {
+            /* This error is not fatal */
+            DEBUG(1, ("add_user_to_delayed_online_authentication failed.\n"));
+        }
+        state->pam_status = PAM_AUTHINFO_UNAVAIL;
+        state->dp_err = DP_ERR_OFFLINE;
+    }
+
+    tevent_req_done(req);
+}
+
 static void krb_reply(struct be_req *req, int dp_err, int result)
 {
     req->fn(req, dp_err, result, NULL);
@@ -1335,4 +1360,3 @@ void krb5_auth_done(struct tevent_req *req)
 
     krb_reply(be_req, dp_err, pd->pam_status);
 }
-
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index e614d5c..61b8071 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -62,6 +62,7 @@ struct krb5child_req {
 };
 
 struct fo_service;
+struct deferred_auth_ctx;
 
 struct krb5_ctx {
     /* opts taken from kinit */
@@ -94,6 +95,8 @@ struct krb5_ctx {
     int child_debug_fd;
 
     pcre *illegal_path_re;
+
+    struct deferred_auth_ctx *deferred_auth_ctx;
 };
 
 void krb5_pam_handler(struct be_req *be_req);
@@ -104,4 +107,11 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
                                   struct pam_data *pd,
                                   struct krb5_ctx *krb5_ctx);
 int krb5_auth_recv(struct tevent_req *req, int *pam_status, int *dp_err);
+
+errno_t add_user_to_delayed_online_authentication(struct krb5_ctx *krb5_ctx,
+                                                  struct pam_data *pd,
+                                                  uid_t uid);
+errno_t init_delayed_online_authentication(struct krb5_ctx *krb5_ctx,
+                                           struct be_ctx *be_ctx,
+                                           struct tevent_context *ev);
 #endif /* __KRB5_AUTH_H__ */
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 17b6511..da7627c 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -42,7 +42,8 @@ struct dp_option default_krb5_opts[] = {
     { "krb5_auth_timeout", DP_OPT_NUMBER, { .number = 15 }, NULL_NUMBER },
     { "krb5_keytab", DP_OPT_STRING, { "/etc/krb5.keytab" }, NULL_STRING },
     { "krb5_validate", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
-    { "krb5_kpasswd", DP_OPT_STRING, NULL_STRING, NULL_STRING }
+    { "krb5_kpasswd", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "krb5_store_password_if_offline", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }
 };
 
 errno_t check_and_export_options(struct dp_option *opts,
diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h
index 12c487a..dd7fdf2 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -53,6 +53,7 @@ enum krb5_opts {
     KRB5_KEYTAB,
     KRB5_VALIDATE,
     KRB5_KPASSWD,
+    KRB5_STORE_PASSWORD_IF_OFFLINE,
 
     KRB5_OPTS
 };
diff --git a/src/providers/krb5/krb5_delayed_online_authentication.c b/src/providers/krb5/krb5_delayed_online_authentication.c
new file mode 100644
index 0000000..02f0991
--- /dev/null
+++ b/src/providers/krb5/krb5_delayed_online_authentication.c
@@ -0,0 +1,354 @@
+/*
+    SSSD
+
+    Kerberos 5 Backend Module -- Request a TGT when the system gets online
+
+    Authors:
+        Sumit Bose <sbose at redhat.com>
+
+    Copyright (C) 2010 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <security/pam_modules.h>
+#ifdef USE_KEYRING
+#include <sys/types.h>
+#include <keyutils.h>
+#endif
+
+#include "providers/krb5/krb5_auth.h"
+#include "dhash.h"
+#include "util/util.h"
+#include "util/find_uid.h"
+
+#define INITIAL_USER_TABLE_SIZE 10
+
+struct deferred_auth_ctx {
+    hash_table_t *user_table;
+    struct be_ctx *be_ctx;
+    struct tevent_context *ev;
+    struct krb5_ctx *krb5_ctx;
+};
+
+struct auth_data {
+    struct be_ctx *be_ctx;
+    struct krb5_ctx *krb5_ctx;
+    struct pam_data *pd;
+};
+
+static void *hash_talloc(const size_t size, void *pvt)
+{
+    return talloc_size(pvt, size);
+}
+
+static void hash_talloc_free(void *ptr, void *pvt)
+{
+    talloc_free(ptr);
+}
+
+static void authenticate_user_done(struct tevent_req *req);
+static void authenticate_user(struct tevent_context *ev,
+                              struct tevent_timer *te,
+                              struct timeval current_time,
+                              void *private_data)
+{
+    struct auth_data *auth_data = talloc_get_type(private_data,
+                                                  struct auth_data);
+    struct pam_data *pd = auth_data->pd;
+    struct tevent_req *req;
+
+    DEBUG_PAM_DATA(9, pd);
+
+    if (pd->authtok == NULL || pd->authtok_size == 0) {
+        DEBUG(1, ("Missing authtok for user [%s].\n", pd->user));
+        return;
+    }
+
+#ifdef USE_KEYRING
+    long keysize;
+    long keyrevoke;
+    int ret;
+    keysize = keyctl_read(pd->key_serial, (char *) pd->authtok,
+                          pd->authtok_size);
+    keyrevoke = keyctl_revoke(pd->key_serial);
+    if (keysize == -1) {
+        ret = errno;
+        DEBUG(1, ("keyctl_read failed [%d][%s].\n", ret, strerror(ret)));
+        return;
+    } else if (keysize != pd->authtok_size) {
+        DEBUG(1, ("keyctl_read returned key with wrong size, "
+                  "expect [%d] got [%d].\n", pd->authtok_size, keysize));
+        return;
+    }
+    if (keyrevoke == -1) {
+        ret = errno;
+        DEBUG(1, ("keyctl_revoke failed [%d][%s].\n", ret, strerror(ret)));
+    }
+#endif
+
+    req = krb5_auth_send(auth_data, ev, auth_data->be_ctx, auth_data->pd,
+                         auth_data->krb5_ctx);
+    if (req == NULL) {
+        DEBUG(1, ("krb5_auth_send failed.\n"));
+        talloc_free(auth_data);
+        return;
+    }
+
+    tevent_req_set_callback(req, authenticate_user_done, auth_data);
+}
+
+static void authenticate_user_done(struct tevent_req *req) {
+    struct auth_data *auth_data = tevent_req_callback_data(req,
+                                                           struct auth_data);
+    int ret;
+    int pam_status = PAM_SYSTEM_ERR;
+    int dp_err;
+
+    ret = krb5_auth_recv(req, &pam_status, &dp_err);
+    talloc_free(req);
+    if (ret) {
+        DEBUG(1, ("krb5_auth request failed.\n"));
+    } else {
+        if (pam_status == PAM_SUCCESS) {
+            DEBUG(4, ("Successfully authenticated user [%s].\n",
+                      auth_data->pd->user));
+        } else {
+            DEBUG(1, ("Failed to authenticate user [%s].\n",
+                      auth_data->pd->user));
+        }
+    }
+
+    talloc_free(auth_data);
+}
+
+static errno_t authenticate_stored_users(
+            struct deferred_auth_ctx *deferred_auth_ctx)
+{
+    int ret;
+    hash_table_t *uid_table;
+    struct hash_iter_context_t *iter;
+    hash_entry_t *entry;
+    hash_key_t key;
+    hash_value_t value;
+    struct pam_data *pd;
+    struct auth_data *auth_data;
+    struct tevent_timer *te;
+
+    ret = get_uid_table(deferred_auth_ctx, &uid_table);
+    if (ret != HASH_SUCCESS) {
+        DEBUG(1, ("get_uid_table failed.\n"));
+        return ret;
+    }
+
+    iter = new_hash_iter_context(deferred_auth_ctx->user_table);
+    if (iter == NULL) {
+        DEBUG(1, ("new_hash_iter_context failed.\n"));
+        return EINVAL;
+    }
+
+    while ((entry = iter->next(iter)) != NULL) {
+        key.type = HASH_KEY_ULONG;
+        key.ul = entry->key.ul;
+        pd = talloc_get_type(entry->value.ptr, struct pam_data);
+
+        ret = hash_lookup(uid_table, &key, &value);
+
+        if (ret == HASH_SUCCESS) {
+            DEBUG(1, ("User [%s] is still logged in, "
+                      "trying online authentication.\n", pd->user));
+
+            auth_data = talloc_zero(deferred_auth_ctx->be_ctx,
+                                    struct auth_data);
+            if (auth_data == NULL) {
+                DEBUG(1, ("talloc_zero failed.\n"));
+            } else {
+                auth_data->pd = talloc_steal(auth_data, pd);
+                auth_data->krb5_ctx = deferred_auth_ctx->krb5_ctx;
+                auth_data->be_ctx = deferred_auth_ctx->be_ctx;
+
+                te = tevent_add_timer(deferred_auth_ctx->ev,
+                                      auth_data, tevent_timeval_current(),
+                                      authenticate_user, auth_data);
+                if (te == NULL) {
+                    DEBUG(1, ("tevent_add_timer failed.\n"));
+                }
+            }
+        } else {
+            DEBUG(1, ("User [%s] is not logged in anymore, "
+                      "discarding online authentication.\n", pd->user));
+            talloc_free(pd);
+        }
+
+        ret = hash_delete(deferred_auth_ctx->user_table,
+                          &entry->key);
+        if (ret != HASH_SUCCESS) {
+            DEBUG(1, ("hash_delete failed [%s].\n",
+                      hash_error_string(ret)));
+        }
+    }
+
+    talloc_free(iter);
+
+    return EOK;
+}
+
+static void delayed_online_authentication_callback(void *private_data)
+{
+    struct deferred_auth_ctx *deferred_auth_ctx =
+            talloc_get_type(private_data, struct deferred_auth_ctx);
+    int ret;
+
+    if (deferred_auth_ctx->user_table == NULL) {
+        DEBUG(1, ("Delayed online authentication activated, "
+                  "but user table does not exists.\n"));
+        return;
+    }
+
+    DEBUG(5, ("Backend is online, starting delayed online authentication.\n"));
+    ret = authenticate_stored_users(deferred_auth_ctx);
+    if (ret != EOK) {
+        DEBUG(1, ("authenticate_stored_users failed.\n"));
+    }
+
+    return;
+}
+
+errno_t add_user_to_delayed_online_authentication(struct krb5_ctx *krb5_ctx,
+                                                  struct pam_data *pd,
+                                                  uid_t uid)
+{
+    int ret;
+    hash_key_t key;
+    hash_value_t value;
+    struct pam_data *new_pd;
+
+    if (krb5_ctx->deferred_auth_ctx == NULL) {
+        DEBUG(1, ("Missing context for delayed online authentication.\n"));
+        return EINVAL;
+    }
+
+    if (krb5_ctx->deferred_auth_ctx->user_table == NULL) {
+        DEBUG(1, ("user_table not available.\n"));
+        return EINVAL;
+    }
+
+    if (pd->authtok_size == 0 || pd->authtok == NULL) {
+        DEBUG(1, ("Missing authtok for user [%s].\n", pd->user));
+        return EINVAL;
+    }
+
+    ret = copy_pam_data(krb5_ctx->deferred_auth_ctx, pd, &new_pd);
+    if (ret != EOK) {
+        DEBUG(1, ("copy_pam_data failed\n"));
+        return ENOMEM;
+    }
+
+
+#ifdef USE_KEYRING
+    new_pd->key_serial = add_key("user", new_pd->user, new_pd->authtok,
+                                 new_pd->authtok_size, KEY_SPEC_THREAD_KEYRING);
+    if (new_pd->key_serial == -1) {
+        ret = errno;
+        DEBUG(1, ("add_key fialed [%d][%s].\n", ret, strerror(ret)));
+        talloc_free(new_pd);
+        return ret;
+    }
+    DEBUG(9, ("Saved authtok of user [%s] with serial [%ld].\n",
+              new_pd->user, new_pd->key_serial));
+    memset(new_pd->authtok, 0, new_pd->authtok_size);
+#endif
+
+    key.type = HASH_KEY_ULONG;
+    key.ul = uid;
+    value.type = HASH_VALUE_PTR;
+    value.ptr = new_pd;
+
+    ret = hash_enter(krb5_ctx->deferred_auth_ctx->user_table,
+                     &key, &value);
+    if (ret != HASH_SUCCESS) {
+        DEBUG(1, ("Cannot add user [%s] to table [%s], "
+                  "delayed online authentication not possible.\n",
+                  pd->user, hash_error_string(ret)));
+        talloc_free(new_pd);
+        return ENOMEM;
+    }
+
+    DEBUG(9, ("Added user [%s] successfully to "
+              "delayed online authentication.\n", pd->user));
+
+    return EOK;
+}
+
+errno_t init_delayed_online_authentication(struct krb5_ctx *krb5_ctx,
+                                           struct be_ctx *be_ctx,
+                                           struct tevent_context *ev)
+{
+    int ret;
+    hash_table_t *tmp_table;
+
+    ret = get_uid_table(krb5_ctx, &tmp_table);
+    if (ret != EOK) {
+        if (ret == ENOSYS) {
+            DEBUG(0, ("Delayed online auth was requested "
+                      "on an unsupported system.\n"));
+        } else {
+            DEBUG(0, ("Delayed online auth was requested "
+                      "but initialisation failed.\n"));
+        }
+        return ret;
+    }
+    ret = hash_destroy(tmp_table);
+    if (ret != HASH_SUCCESS) {
+        DEBUG(1, ("hash_destroy failed [%s].\n", hash_error_string(ret)));
+        return EFAULT;
+    }
+
+    krb5_ctx->deferred_auth_ctx = talloc_zero(krb5_ctx,
+                                          struct deferred_auth_ctx);
+    if (krb5_ctx->deferred_auth_ctx == NULL) {
+        DEBUG(1, ("talloc_zero failed.\n"));
+        return ENOMEM;
+    }
+
+    ret = hash_create_ex(INITIAL_USER_TABLE_SIZE,
+                         &krb5_ctx->deferred_auth_ctx->user_table,
+                         0, 0, 0, 0, hash_talloc, hash_talloc_free,
+                         krb5_ctx->deferred_auth_ctx,
+                         NULL, NULL);
+    if (ret != HASH_SUCCESS) {
+        DEBUG(1, ("hash_create_ex failed [%s]\n", hash_error_string(ret)));
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    krb5_ctx->deferred_auth_ctx->be_ctx = be_ctx;
+    krb5_ctx->deferred_auth_ctx->krb5_ctx = krb5_ctx;
+    krb5_ctx->deferred_auth_ctx->ev = ev;
+
+    ret = be_add_online_cb(krb5_ctx, be_ctx,
+                           delayed_online_authentication_callback,
+                           krb5_ctx->deferred_auth_ctx, NULL);
+    if (ret != EOK) {
+        DEBUG(1, ("be_add_online_cb failed.\n"));
+        goto fail;
+    }
+
+    /* TODO: add destructor */
+
+    return EOK;
+fail:
+    talloc_zfree(krb5_ctx->deferred_auth_ctx);
+    return ret;
+}
diff --git a/src/providers/krb5/krb5_init.c b/src/providers/krb5/krb5_init.c
index 0ad5892..d0c8be5 100644
--- a/src/providers/krb5/krb5_init.c
+++ b/src/providers/krb5/krb5_init.c
@@ -121,6 +121,14 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
         }
     }
 
+    if (dp_opt_get_bool(ctx->opts, KRB5_STORE_PASSWORD_IF_OFFLINE)) {
+        ret = init_delayed_online_authentication(ctx, bectx, bectx->ev);
+        if (ret != EOK) {
+            DEBUG(1, ("init_delayed_online_authentication failed.\n"));
+            goto fail;
+        }
+    }
+
     ret = check_and_export_options(ctx->opts, bectx->domain);
     if (ret != EOK) {
         DEBUG(1, ("check_and_export_options failed.\n"));
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index d5b4eff..43da44b 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -381,7 +381,7 @@ static void pam_reply(struct pam_auth_req *preq)
                     ret = sysdb_cache_auth(preq, sysdb,
                                            preq->domain, pd->user,
                                            pd->authtok, pd->authtok_size,
-                                           pctx->rctx->cdb,
+                                           pctx->rctx->cdb, false,
                                            &exp_date, &delay_until);
 
                     pam_cache_auth_done(preq, ret, exp_date, delay_until);
diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
index 3606094..44cb6dc 100644
--- a/src/tests/sysdb-tests.c
+++ b/src/tests/sysdb-tests.c
@@ -1425,7 +1425,7 @@ static void cached_authentication_without_expiration(const char *username,
     ret = sysdb_cache_auth(data, test_ctx->sysdb,
                            test_ctx->domain, data->username,
                            (const uint8_t *)password, strlen(password),
-                           test_ctx->confdb, &expire_date, &delayed_until);
+                           test_ctx->confdb, false, &expire_date, &delayed_until);
 
     fail_unless(ret == expected_result, "sysdb_cache_auth request does not "
                                         "return expected result [%d].",
@@ -1487,7 +1487,7 @@ static void cached_authentication_with_expiration(const char *username,
     ret = sysdb_cache_auth(data, test_ctx->sysdb,
                            test_ctx->domain, data->username,
                            (const uint8_t *) password, strlen(password),
-                           test_ctx->confdb, &expire_date, &delayed_until);
+                           test_ctx->confdb, false, &expire_date, &delayed_until);
 
     fail_unless(ret == expected_result,
                 "sysdb_cache_auth request does not return expected "
-- 
1.6.6.1

-------------- next part --------------
From 37da1e7cf4dbe91b166095e9c96a90a64a7a64f3 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 25 May 2010 13:59:41 +0200
Subject: [PATCH 2/2] Fix handling of ccache file when going offline

The ccache file was removed too early if system is offline but the
backend was not already marked offline. Now we remove the ccache file
only if the successfully got a new one and it is not the same as the old
one.
---
 src/providers/krb5/krb5_auth.c |  107 ++++++++++++++++++++++++++++------------
 src/providers/krb5/krb5_auth.h |    1 +
 2 files changed, 76 insertions(+), 32 deletions(-)

diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index a7aebe5..d43bb02 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -45,6 +45,49 @@
 #define KRB5_CHILD SSSD_LIBEXEC_PATH"/krb5_child"
 #endif
 
+static errno_t safe_remove_old_ccache_file(const char *old_ccache_file,
+                                           const char *new_ccache_file)
+{
+    int ret;
+    size_t old_offset = 0;
+    size_t new_offset = 0;
+
+    if (new_ccache_file == NULL) {
+        DEBUG(1, ("Missing new ccache file, "
+                  "old ccache file is not deleted.\n"));
+        return EINVAL;
+    }
+
+    if (old_ccache_file != NULL) {
+        if (strncmp(old_ccache_file, "FILE:", 5) == 0) {
+            old_offset = 5;
+        }
+        if (strncmp(new_ccache_file, "FILE:", 5) == 0) {
+            new_offset = 5;
+        }
+        if (strcmp(old_ccache_file + old_offset,
+                   new_ccache_file + new_offset) == 0) {
+            DEBUG(7, ("New and old ccache file are the same, "
+                      "no one will be deleted.\n"));
+            return EOK;
+        }
+        if (old_ccache_file[old_offset] != '/') {
+            DEBUG(1, ("Ccache file name [%s] is not an absolute path.\n",
+                      old_ccache_file + old_offset));
+            return EINVAL;
+        }
+        ret = unlink(old_ccache_file + old_offset);
+        if (ret == -1 && errno != ENOENT) {
+            ret = errno;
+            DEBUG(1, ("unlink [%s] failed [%d][%s].\n", old_ccache_file, ret,
+                                                        strerror(ret)));
+            return ret;
+        }
+    }
+
+    return EOK;
+}
+
 static errno_t check_if_ccache_file_is_used(uid_t uid, const char *ccname,
                                             bool *result)
 {
@@ -734,7 +777,18 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
                   ccache_file ? ccache_file : "not set",
                   kr->active_ccache_present ? "" : "not",
                   kr->valid_tgt_present ? "" : "not"));
-        kr->ccname = ccache_file;
+        if (ccache_file != NULL) {
+            kr->ccname = ccache_file;
+            kr->old_ccname = talloc_strdup(kr, ccache_file);
+            if (kr->old_ccname == NULL) {
+                DEBUG(1, ("talloc_strdup failed.\n"));
+                ret = ENOMEM;
+                goto done;
+            }
+        } else {
+            kr->ccname = NULL;
+            kr->old_ccname = NULL;
+        }
         break;
 
     default:
@@ -837,44 +891,29 @@ static void krb5_find_ccache_step(struct tevent_req *req)
     struct krb5child_req *kr = state->kr;
     struct pam_data *pd = kr->pd;
     char *msg;
-    size_t offset = 0;
     bool private_path = false;
     struct tevent_req *subreq = NULL;
 
+    if (!kr->is_offline) {
+        kr->is_offline = be_is_offline(state->be_ctx);
+    }
+
     /* The ccache file should be (re)created if one of the following conditions
      * is true:
      * - it doesn't exist (kr->ccname == NULL)
      * - the backend is online and the current ccache file is not used, i.e
      *   the related user is currently not logged in
-     *   (!be_is_offline(state->be_ctx) && !kr->active_ccache_present)
+     *   (!kr->is_offline && !kr->active_ccache_present)
      * - the backend is offline and the current cache file not used and
      *   it does not contain a valid tgt
-     *   (be_is_offline(state->be_ctx) &&
+     *   (kr->is_offline &&
      *    !kr->active_ccache_present && !kr->valid_tgt_present)
      */
     if (kr->ccname == NULL ||
-        (be_is_offline(state->be_ctx) && !kr->active_ccache_present &&
+        (kr->is_offline && !kr->active_ccache_present &&
             !kr->valid_tgt_present) ||
-        (!be_is_offline(state->be_ctx) && !kr->active_ccache_present)) {
+        (!kr->is_offline && !kr->active_ccache_present)) {
             DEBUG(9, ("Recreating  ccache file.\n"));
-            if (kr->ccname != NULL) {
-                if (strncmp(kr->ccname, "FILE:", 5) == 0) {
-                    offset = 5;
-                }
-                if (kr->ccname[offset] != '/') {
-                    DEBUG(1, ("Ccache file name [%s] is not an absolute path.\n",
-                              kr->ccname + offset));
-                    ret = EINVAL;
-                    goto done;
-                }
-                ret = unlink(kr->ccname + offset);
-                if (ret == -1 && errno != ENOENT) {
-                    ret = errno;
-                    DEBUG(1, ("unlink [%s] failed [%d][%s].\n", kr->ccname, ret,
-                                                                strerror(ret)));
-                    goto done;
-                }
-            }
             kr->ccname = expand_ccname_template(kr, kr,
                                           dp_opt_get_cstring(kr->krb5_ctx->opts,
                                                              KRB5_CCNAME_TMPL),
@@ -894,13 +933,13 @@ static void krb5_find_ccache_step(struct tevent_req *req)
             }
     }
 
-    if (be_is_offline(state->be_ctx)) {
+    if (kr->is_offline) {
         DEBUG(9, ("Preparing for offline operation.\n"));
-        kr->is_offline = true;
 
         if (kr->valid_tgt_present || kr->active_ccache_present) {
             DEBUG(9, ("Valid TGT available or "
                       "ccache file is already in use.\n"));
+            kr->ccname = kr->old_ccname;
             msg = talloc_asprintf(pd, "%s=%s", CCACHE_ENV_NAME, kr->ccname);
             if (msg == NULL) {
                 DEBUG(1, ("talloc_asprintf failed.\n"));
@@ -1094,19 +1133,23 @@ static void krb5_child_done(struct tevent_req *subreq)
         fo_set_port_status(kr->srv, PORT_WORKING);
     }
 
-    /* The following cases are left now:
-     *  - offline (msg_status == PAM_AUTHINFO_UNAVAIL or
-     *             msg_status == PAM_AUTHTOK_LOCK_BUSY)
-     *  - successful authentication or password change
+    /* Now only a successful authentication or password change is left.
      *
-     *  For all these cases we expect that one of the messages for the
-     *  received buffer contains the name of the credential cache file. */
+     * We expect that one of the messages in the received buffer contains
+     * the name of the credential cache file. */
     if (kr->ccname == NULL) {
         DEBUG(1, ("Missing ccache name in child response.\n"));
         ret = EINVAL;
         goto done;
     }
 
+    if (kr->old_ccname != NULL) {
+        ret = safe_remove_old_ccache_file(kr->old_ccname, kr->ccname);
+        if (ret != EOK) {
+            DEBUG(1, ("Failed to remove old ccache file [%s], please remove it manually.\n"));
+        }
+    }
+
     struct sysdb_attrs *attrs;
     attrs = sysdb_new_attrs(state);
     ret = sysdb_attrs_add_string(attrs, SYSDB_CCACHE_FILE, kr->ccname);
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index 61b8071..ec54f73 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -50,6 +50,7 @@ struct krb5child_req {
     struct tevent_timer *timeout_handler;
 
     const char *ccname;
+    const char *old_ccname;
     const char *homedir;
     const char *upn;
     uid_t uid;
-- 
1.6.6.1



More information about the sssd-devel mailing list