[SSSD] [PATCHES] Support automatic Kerberos ticket renewal

Sumit Bose sbose at redhat.com
Fri Dec 3 14:16:53 UTC 2010


On Fri, Dec 03, 2010 at 08:57:02AM -0500, Stephen Gallagher wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> On 12/03/2010 05:07 AM, Sumit Bose wrote:
> >> Thanks for the review, new versions attached.
> 
> Patches 0001-0007 and 0009: Ack.
> 
> Patch 0008: As discussed on IRC, please add a flag to ensure we don't
> queue multiple going_online callbacks for renewal.

New version of patch 8 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/
> 
> iEYEARECAAYFAkz49y4ACgkQeiVVYja6o6P5VgCgrOyj3GyEK+4C2yy67OImjm42
> C5sAn1XfKJom5vSQPqZSY9ETPcthj+Xp
> =SY65
> -----END PGP SIGNATURE-----
> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://fedorahosted.org/mailman/listinfo/sssd-devel
-------------- next part --------------
From 3ab1310878526f740a20325f4264f556e9010f23 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 15 Nov 2010 13:46:17 +0100
Subject: [PATCH 8/9] Add support for automatic Kerberos ticket renewal

---
 Makefile.am                              |    2 +
 src/config/SSSDConfig.py                 |    1 +
 src/config/SSSDConfigTest.py             |    9 +-
 src/config/etc/sssd.api.d/sssd-krb5.conf |    1 +
 src/man/sssd-krb5.5.xml                  |   18 ++
 src/providers/ipa/ipa_common.c           |    3 +-
 src/providers/ipa/ipa_common.h           |    2 +-
 src/providers/krb5/krb5_auth.c           |   18 ++
 src/providers/krb5/krb5_auth.h           |    5 +
 src/providers/krb5/krb5_child_handler.c  |    2 +
 src/providers/krb5/krb5_common.c         |    3 +-
 src/providers/krb5/krb5_common.h         |    3 +
 src/providers/krb5/krb5_init.c           |   10 +
 src/providers/krb5/krb5_renew_tgt.c      |  380 ++++++++++++++++++++++++++++++
 14 files changed, 451 insertions(+), 6 deletions(-)
 create mode 100644 src/providers/krb5/krb5_renew_tgt.c

diff --git a/Makefile.am b/Makefile.am
index 4a8fd59..95a130a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -824,6 +824,7 @@ libsss_krb5_la_SOURCES = \
     src/providers/krb5/krb5_utils.c \
     src/providers/krb5/krb5_become_user.c \
     src/providers/krb5/krb5_delayed_online_authentication.c \
+    src/providers/krb5/krb5_renew_tgt.c \
     src/providers/krb5/krb5_auth.c \
     src/providers/krb5/krb5_access.c \
     src/providers/krb5/krb5_child_handler.c \
@@ -871,6 +872,7 @@ libsss_ipa_la_SOURCES = \
     src/providers/krb5/krb5_utils.c \
     src/providers/krb5/krb5_become_user.c \
     src/providers/krb5/krb5_delayed_online_authentication.c \
+    src/providers/krb5/krb5_renew_tgt.c \
     src/providers/krb5/krb5_common.c \
     src/providers/krb5/krb5_auth.c \
     src/providers/krb5/krb5_access.c \
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index 1286e54..edfc823 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -106,6 +106,7 @@ option_strings = {
     'krb5_store_password_if_offline' : _("Store password if offline for later online authentication"),
     'krb5_renewable_lifetime' : _("Renewable lifetime of the TGT"),
     'krb5_lifetime' : _("Lifetime of the TGT"),
+    'krb5_renew_interval' : _("Time between two checks for renewal"),
 
     # [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 3d57108..b51614d 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -551,7 +551,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
              'krb5_store_password_if_offline',
              'krb5_auth_timeout',
              'krb5_renewable_lifetime',
-             'krb5_lifetime'])
+             'krb5_lifetime',
+             'krb5_renew_interval'])
 
         options = domain.list_options()
 
@@ -727,7 +728,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'krb5_store_password_if_offline',
             'krb5_auth_timeout',
             'krb5_renewable_lifetime',
-            'krb5_lifetime']
+            'krb5_lifetime',
+            'krb5_renew_interval']
 
         self.assertTrue(type(options) == dict,
                         "Options should be a dictionary")
@@ -877,7 +879,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
              'krb5_store_password_if_offline',
              'krb5_auth_timeout',
              'krb5_renewable_lifetime',
-             'krb5_lifetime'])
+             'krb5_lifetime',
+             'krb5_renew_interval'])
 
         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 f824cfb..86eff20 100644
--- a/src/config/etc/sssd.api.d/sssd-krb5.conf
+++ b/src/config/etc/sssd.api.d/sssd-krb5.conf
@@ -13,6 +13,7 @@ krb5_validate = bool, None, false
 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
 
 [provider/krb5/chpass]
 
diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml
index ff5d0a6..a499d75 100644
--- a/src/man/sssd-krb5.5.xml
+++ b/src/man/sssd-krb5.5.xml
@@ -332,6 +332,24 @@
                     </listitem>
                 </varlistentry>
 
+                <varlistentry>
+                    <term>krb5_renew_interval (integer)</term>
+                    <listitem>
+                        <para>
+                            The time in seconds between two checks if the TGT
+                            should be renewed. TGTs are renewed if about half
+                            of their lifetime is exceeded.
+                        </para>
+                        <para>
+                             If this option is not set or 0 the automatic
+                             renewal is disabled.
+                        </para>
+                        <para>
+                            Default: not set
+                        </para>
+                    </listitem>
+                </varlistentry>
+
             </variablelist>
         </para>
     </refsect1>
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 545ddc9..786be83 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -141,7 +141,8 @@ struct dp_option ipa_def_krb5_opts[] = {
     { "krb5_kpasswd", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "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_lifetime", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "krb5_renew_interval", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER }
 };
 
 int ipa_get_options(TALLOC_CTX *memctx,
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index be0f361..f0d4ad8 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 11
+#define IPA_KRB5_OPTS_TEST 12
 
 enum ipa_basic_opt {
     IPA_DOMAIN = 0,
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 974e768..515c181 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -210,6 +210,7 @@ static struct krb5_ctx *get_krb5_ctx(struct be_req *be_req)
 
     switch (pd->cmd) {
         case SSS_PAM_AUTHENTICATE:
+        case SSS_CMD_RENEW:
             return talloc_get_type(be_req->be_ctx->bet_info[BET_AUTH].pvt_bet_data,
                                    struct krb5_ctx);
             break;
@@ -327,6 +328,7 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
 
     switch (pd->cmd) {
         case SSS_PAM_AUTHENTICATE:
+        case SSS_CMD_RENEW:
         case SSS_PAM_CHAUTHTOK:
             break;
         case SSS_PAM_CHAUTHTOK_PRELIM:
@@ -786,6 +788,19 @@ static void krb5_child_done(struct tevent_req *subreq)
         }
     }
 
+    if (msg_status == PAM_SUCCESS &&
+        dp_opt_get_int(kr->krb5_ctx->opts, KRB5_RENEW_INTERVAL) > 0 &&
+        (pd->cmd == SSS_PAM_AUTHENTICATE || pd->cmd == SSS_CMD_RENEW ||
+         pd->cmd == SSS_PAM_CHAUTHTOK) &&
+        tgtt.renew_till > tgtt.endtime && kr->ccname != NULL) {
+        DEBUG(7, ("Adding [%s] for automatic renewal.\n", kr->ccname));
+        ret = add_tgt_to_renew_table(kr->krb5_ctx, kr->ccname, &tgtt, pd);
+        if (ret != EOK) {
+            DEBUG(1, ("add_tgt_to_renew_table failed, "
+                      "automatic renewal not possible.\n"));
+        }
+    }
+
     /* If the child request failed, but did not return an offline error code,
      * return with the status */
     if (msg_status != PAM_SUCCESS && msg_status != PAM_AUTHINFO_UNAVAIL &&
@@ -891,6 +906,7 @@ static struct tevent_req *krb5_next_server(struct tevent_req *req)
 
     switch (pd->cmd) {
         case SSS_PAM_AUTHENTICATE:
+        case SSS_CMD_RENEW:
             fo_set_port_status(state->kr->srv, PORT_NOT_WORKING);
             next_req = krb5_next_kdc(req);
             break;
@@ -975,6 +991,7 @@ static void krb5_save_ccname_done(struct tevent_req *req)
 
         switch(pd->cmd) {
             case SSS_PAM_AUTHENTICATE:
+            case SSS_CMD_RENEW:
             case SSS_PAM_CHAUTHTOK_PRELIM:
                 password = talloc_size(state, pd->authtok_size + 1);
                 if (password != NULL) {
@@ -1077,6 +1094,7 @@ void krb5_pam_handler(struct be_req *be_req)
 
     switch (pd->cmd) {
         case SSS_PAM_AUTHENTICATE:
+        case SSS_CMD_RENEW:
         case SSS_PAM_CHAUTHTOK_PRELIM:
         case SSS_PAM_CHAUTHTOK:
             req = krb5_auth_send(be_req, be_req->be_ctx->ev, be_req->be_ctx, pd,
diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h
index 130e85d..f40cbcb 100644
--- a/src/providers/krb5/krb5_auth.h
+++ b/src/providers/krb5/krb5_auth.h
@@ -80,6 +80,11 @@ errno_t init_delayed_online_authentication(struct krb5_ctx *krb5_ctx,
                                            struct be_ctx *be_ctx,
                                            struct tevent_context *ev);
 
+errno_t init_renew_tgt(struct krb5_ctx *krb5_ctx, struct be_ctx *be_ctx,
+                       struct tevent_context *ev, time_t renew_intv);
+errno_t add_tgt_to_renew_table(struct krb5_ctx *krb5_ctx, const char *ccfile,
+                               struct tgt_times *tgtt, struct pam_data *pd);
+
 /* krb5_access.c */
 struct tevent_req *krb5_access_send(TALLOC_CTX *mem_ctx,
                                     struct tevent_context *ev,
diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
index 9da8a37..bafa0bb 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -104,6 +104,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
     buf->size = 6*sizeof(uint32_t) + strlen(kr->upn);
 
     if (kr->pd->cmd == SSS_PAM_AUTHENTICATE ||
+        kr->pd->cmd == SSS_CMD_RENEW ||
         kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ||
         kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
         buf->size += 4*sizeof(uint32_t) + strlen(kr->ccname) + strlen(keytab) +
@@ -137,6 +138,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
     safealign_memcpy(&buf->data[rp], kr->upn, strlen(kr->upn), &rp);
 
     if (kr->pd->cmd == SSS_PAM_AUTHENTICATE ||
+        kr->pd->cmd == SSS_CMD_RENEW ||
         kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ||
         kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
         SAFEALIGN_SET_UINT32(&buf->data[rp], strlen(kr->ccname), &rp);
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 7ee4d09..cdc8437 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_kpasswd", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "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_lifetime", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "krb5_renew_interval", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER }
 };
 
 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 68e4426..c20c7b3 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -56,6 +56,7 @@ enum krb5_opts {
     KRB5_STORE_PASSWORD_IF_OFFLINE,
     KRB5_RENEWABLE_LIFETIME,
     KRB5_LIFETIME,
+    KRB5_RENEW_INTERVAL,
 
     KRB5_OPTS
 };
@@ -77,6 +78,7 @@ struct krb5_service {
 
 struct fo_service;
 struct deferred_auth_ctx;
+struct renew_tgt_ctx;
 
 struct krb5_ctx {
     /* opts taken from kinit */
@@ -111,6 +113,7 @@ struct krb5_ctx {
     pcre *illegal_path_re;
 
     struct deferred_auth_ctx *deferred_auth_ctx;
+    struct renew_tgt_ctx *renew_tgt_ctx;
 };
 
 struct remove_info_files_ctx {
diff --git a/src/providers/krb5/krb5_init.c b/src/providers/krb5/krb5_init.c
index f2b5dd7..0f1ed41 100644
--- a/src/providers/krb5/krb5_init.c
+++ b/src/providers/krb5/krb5_init.c
@@ -56,6 +56,7 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
     const char *errstr;
     int errval;
     int errpos;
+    time_t renew_intv;
 
     if (krb5_options == NULL) {
         krb5_options = talloc_zero(bectx, struct krb5_options);
@@ -128,6 +129,15 @@ int sssm_krb5_auth_init(struct be_ctx *bectx,
         }
     }
 
+    renew_intv = dp_opt_get_int(ctx->opts, KRB5_RENEW_INTERVAL);
+    if (renew_intv > 0) {
+        ret = init_renew_tgt(ctx, bectx, bectx->ev, renew_intv);
+        if (ret != EOK) {
+            DEBUG(1, ("init_renew_tgt 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/providers/krb5/krb5_renew_tgt.c b/src/providers/krb5/krb5_renew_tgt.c
new file mode 100644
index 0000000..be029fd
--- /dev/null
+++ b/src/providers/krb5/krb5_renew_tgt.c
@@ -0,0 +1,380 @@
+/*
+    SSSD
+
+    Kerberos 5 Backend Module -- Renew a TGT automatically
+
+    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>
+
+#include "util/util.h"
+#include "providers/krb5/krb5_common.h"
+#include "providers/krb5/krb5_auth.h"
+
+#define INITIAL_TGT_TABLE_SIZE 10
+
+struct renew_tgt_ctx {
+    hash_table_t *tgt_table;
+    struct be_ctx *be_ctx;
+    struct tevent_context *ev;
+    struct krb5_ctx *krb5_ctx;
+    time_t timer_interval;
+    struct tevent_timer *te;
+    bool added_to_online_callbacks;
+};
+
+struct renew_data {
+    time_t start_time;
+    time_t lifetime;
+    time_t start_renew_at;
+    struct pam_data *pd;
+};
+
+struct auth_data {
+    struct be_ctx *be_ctx;
+    struct krb5_ctx *krb5_ctx;
+    struct pam_data *pd;
+    hash_table_t *table;
+    hash_key_t key;
+};
+
+
+static void renew_tgt_done(struct tevent_req *req);
+static void renew_tgt(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 tevent_req *req;
+
+    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, renew_tgt_done, auth_data);
+}
+
+static void renew_tgt_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 {
+        switch (pam_status) {
+            case PAM_SUCCESS:
+                DEBUG(4, ("Successfully renewed TGT for user [%s].\n",
+                          auth_data->pd->user));
+                break;
+            case PAM_AUTHINFO_UNAVAIL:
+            case PAM_AUTHTOK_LOCK_BUSY:
+                DEBUG(4, ("Cannot renewed TGT for user [%s] while offline, "
+                          "will retry later.\n",
+                          auth_data->pd->user));
+                break;
+            default:
+                DEBUG(1, ("Failed to renew TGT for user [%s].\n",
+                          auth_data->pd->user));
+                ret = hash_delete(auth_data->table, &auth_data->key);
+                if (ret != HASH_SUCCESS) {
+                    DEBUG(1, ("hash_delete failed.\n"));
+                }
+        }
+    }
+
+    talloc_zfree(auth_data);
+}
+
+static errno_t renew_all_tgts(struct renew_tgt_ctx *renew_tgt_ctx)
+{
+    int ret;
+    hash_entry_t *entries;
+    unsigned long count;
+    size_t c;
+    time_t now;
+    struct auth_data *auth_data;
+    struct renew_data *renew_data;
+    struct tevent_timer *te;
+
+    ret = hash_entries(renew_tgt_ctx->tgt_table, &count, &entries);
+    if (ret != HASH_SUCCESS) {
+        DEBUG(1, ("hash_entries failed.\n"));
+        return ENOMEM;
+    }
+
+    now = time(NULL);
+
+    for (c = 0; c < count; c++) {
+        renew_data = talloc_get_type(entries[c].value.ptr, struct renew_data);
+        DEBUG(9, ("Checking [%s] for renewal at [%.24s].\n", entries[c].key.str,
+                  ctime(&renew_data->start_renew_at)));
+        if (renew_data->start_renew_at < now) {
+            auth_data = talloc_zero(renew_tgt_ctx, struct auth_data);
+            if (auth_data == NULL) {
+                DEBUG(1, ("talloc_zero failed.\n"));
+            } else {
+                auth_data->pd = renew_data->pd;
+                auth_data->krb5_ctx = renew_tgt_ctx->krb5_ctx;
+                auth_data->be_ctx = renew_tgt_ctx->be_ctx;
+                auth_data->table = renew_tgt_ctx->tgt_table;
+                auth_data->key.type = entries[c].key.type;
+                auth_data->key.str = talloc_strdup(auth_data,
+                                                   entries[c].key.str);
+                if (auth_data->key.str == NULL) {
+                    DEBUG(1, ("talloc_strdup failed.\n"));
+                    te = NULL;
+                } else {
+                    te = tevent_add_timer(renew_tgt_ctx->ev,
+                                          auth_data, tevent_timeval_current(),
+                                          renew_tgt, auth_data);
+                    if (te == NULL) {
+                        DEBUG(1, ("tevent_add_timer failed.\n"));
+                    }
+                }
+            }
+
+            if (auth_data == NULL || te == NULL) {
+                DEBUG(1, ("Failed to renew TGT in [%s].\n", entries[c].key.str));
+                ret = hash_delete(renew_tgt_ctx->tgt_table, &entries[c].key);
+                if (ret != HASH_SUCCESS) {
+                    DEBUG(1, ("hash_delete failed.\n"));
+                }
+            }
+        }
+    }
+
+    talloc_free(entries);
+
+    return EOK;
+}
+
+static void renew_handler(struct renew_tgt_ctx *renew_tgt_ctx);
+
+static void renew_tgt_online_callback(void *private_data)
+{
+    struct renew_tgt_ctx *renew_tgt_ctx = talloc_get_type(private_data,
+                                                          struct renew_tgt_ctx);
+
+    renew_tgt_ctx->added_to_online_callbacks = false;
+    renew_handler(renew_tgt_ctx);
+}
+
+static void renew_tgt_timer_handler(struct tevent_context *ev,
+                                    struct tevent_timer *te,
+                                    struct timeval current_time, void *data)
+{
+    struct renew_tgt_ctx *renew_tgt_ctx = talloc_get_type(data,
+                                                          struct renew_tgt_ctx);
+
+    renew_handler(renew_tgt_ctx);
+}
+
+static void renew_handler(struct renew_tgt_ctx *renew_tgt_ctx)
+{
+    struct timeval next;
+    int ret;
+
+    if (be_is_offline(renew_tgt_ctx->be_ctx)) {
+        if (renew_tgt_ctx->added_to_online_callbacks) {
+            DEBUG(3, ("Renewal task was already added to online callbacks.\n"));
+            return;
+        }
+        DEBUG(7, ("Offline, adding renewal task to online callbacks.\n"));
+        ret = be_add_online_cb(renew_tgt_ctx->krb5_ctx, renew_tgt_ctx->be_ctx,
+                               renew_tgt_online_callback, renew_tgt_ctx, NULL);
+        if (ret == EOK) {
+            renew_tgt_ctx->added_to_online_callbacks = true;
+            return;
+        }
+
+        DEBUG(1, ("Failed to add the renewal task to online callbacks, "
+                  "continue normal operation.\n"));
+    } else {
+        ret = renew_all_tgts(renew_tgt_ctx);
+        if (ret != EOK) {
+            DEBUG(1, ("renew_all_tgts failed. "
+                      "Disabling automatic TGT renewal\n"));
+            sss_log(SSS_LOG_ERR, "Disabling automatic TGT renewal.");
+            talloc_zfree(renew_tgt_ctx);
+            return;
+        }
+    }
+
+    DEBUG(7, ("Adding new renew timer.\n"));
+
+    next = tevent_timeval_current_ofs(renew_tgt_ctx->timer_interval,
+                                      0);
+    renew_tgt_ctx->te = tevent_add_timer(renew_tgt_ctx->ev, renew_tgt_ctx,
+                                         next, renew_tgt_timer_handler,
+                                         renew_tgt_ctx);
+    if (renew_tgt_ctx->te == NULL) {
+        DEBUG(1, ("tevent_add_timer failed.\n"));
+        sss_log(SSS_LOG_ERR, "Disabling automatic TGT renewal.");
+        talloc_zfree(renew_tgt_ctx);
+    }
+
+    return;
+}
+
+errno_t init_renew_tgt(struct krb5_ctx *krb5_ctx, struct be_ctx *be_ctx,
+                       struct tevent_context *ev, time_t renew_intv)
+{
+    int ret;
+    struct timeval next;
+
+    krb5_ctx->renew_tgt_ctx = talloc_zero(krb5_ctx, struct renew_tgt_ctx);
+    if (krb5_ctx->renew_tgt_ctx == NULL) {
+        DEBUG(1, ("talloc_zero failed.\n"));
+        return ENOMEM;
+    }
+
+    ret = sss_hash_create(krb5_ctx->renew_tgt_ctx, INITIAL_TGT_TABLE_SIZE,
+                          &krb5_ctx->renew_tgt_ctx->tgt_table);
+    if (ret != EOK) {
+        DEBUG(1, ("sss_hash_create failed.\n"));
+        goto fail;
+    }
+
+    krb5_ctx->renew_tgt_ctx->be_ctx = be_ctx;
+    krb5_ctx->renew_tgt_ctx->krb5_ctx = krb5_ctx;
+    krb5_ctx->renew_tgt_ctx->ev = ev;
+    krb5_ctx->renew_tgt_ctx->timer_interval = renew_intv;
+    krb5_ctx->renew_tgt_ctx->added_to_online_callbacks = false;
+
+
+    next = tevent_timeval_current_ofs(krb5_ctx->renew_tgt_ctx->timer_interval,
+                                      0);
+    krb5_ctx->renew_tgt_ctx->te = tevent_add_timer(ev, krb5_ctx->renew_tgt_ctx,
+                                                   next, renew_tgt_timer_handler,
+                                                   krb5_ctx->renew_tgt_ctx);
+    if (krb5_ctx->renew_tgt_ctx->te == NULL) {
+        DEBUG(1, ("tevent_add_timer failed.\n"));
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    return EOK;
+
+fail:
+    talloc_zfree(krb5_ctx->renew_tgt_ctx);
+    return ret;
+}
+
+errno_t add_tgt_to_renew_table(struct krb5_ctx *krb5_ctx, const char *ccfile,
+                               struct tgt_times *tgtt, struct pam_data *pd)
+{
+    char *key_str = NULL;
+    int ret;
+    hash_key_t key;
+    hash_value_t value;
+    struct renew_data *renew_data = NULL;
+
+    if (krb5_ctx->renew_tgt_ctx == NULL) {
+        DEBUG(7 ,("Renew context not initialized, "
+                  "automatic renewal not available.\n"));
+        return EOK;
+    }
+
+    if (pd->cmd != SSS_PAM_AUTHENTICATE && pd->cmd != SSS_CMD_RENEW &&
+        pd->cmd != SSS_PAM_CHAUTHTOK) {
+        DEBUG(1, ("Unexpected pam task [%d].\n", pd->cmd));
+        return EINVAL;
+    }
+
+    key.type = HASH_KEY_STRING;
+    if (ccfile[0] == '/') {
+        key_str = talloc_asprintf(NULL, "FILE:%s", ccfile);
+        if (key_str == NULL) {
+            DEBUG(1, ("talloc_asprintf doneed.\n"));
+            ret = ENOMEM;
+            goto done;
+        }
+    } else {
+        key_str = talloc_strdup(NULL, ccfile);
+    }
+    key.str = key_str;
+
+    renew_data = talloc_zero(krb5_ctx->renew_tgt_ctx, struct renew_data);
+    if (renew_data == NULL) {
+        DEBUG(1, ("talloc_zero doneed.\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+
+    renew_data->start_time = tgtt->starttime;
+    renew_data->lifetime = tgtt->endtime;
+    renew_data->start_renew_at = (time_t) (tgtt->starttime +
+                                        0.5 *(tgtt->endtime - tgtt->starttime));
+
+    ret = copy_pam_data(renew_data, pd, &renew_data->pd);
+    if (ret != EOK) {
+        DEBUG(1, ("copy_pam_data doneed.\n"));
+        goto done;
+    }
+
+    if (renew_data->pd->newauthtok_type != SSS_AUTHTOK_TYPE_EMPTY) {
+        talloc_zfree(renew_data->pd->newauthtok);
+        renew_data->pd->newauthtok_size = 0;
+        renew_data->pd->newauthtok_type = SSS_AUTHTOK_TYPE_EMPTY;
+    }
+
+    talloc_zfree(renew_data->pd->authtok);
+    renew_data->pd->authtok = (uint8_t *) talloc_strdup(renew_data->pd, key.str);
+    if (renew_data->pd->authtok == NULL) {
+        DEBUG(1, ("talloc_strdup failed.\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+    renew_data->pd->authtok_size = strlen((char *) renew_data->pd->authtok) + 1;
+    renew_data->pd->authtok_type = SSS_AUTHTOK_TYPE_CCFILE;
+
+    renew_data->pd->cmd = SSS_CMD_RENEW;
+
+    value.type = HASH_VALUE_PTR;
+    value.ptr = renew_data;
+
+    ret = hash_enter(krb5_ctx->renew_tgt_ctx->tgt_table, &key, &value);
+    if (ret != HASH_SUCCESS) {
+        DEBUG(1, ("hash_enter failed.\n"));
+        ret = EFAULT;
+        goto done;
+    }
+
+    DEBUG(7, ("Added [%s] for renewal at [%.24s].\n", key_str,
+                                           ctime(&renew_data->start_renew_at)));
+
+    ret = EOK;
+
+done:
+    talloc_free(key_str);
+    if (ret != EOK) {
+        talloc_free(renew_data);
+    }
+    return ret;
+}
-- 
1.7.3.2



More information about the sssd-devel mailing list