[SSSD] [PATCHES] Support automatic Kerberos ticket renewal

Sumit Bose sbose at redhat.com
Fri Dec 3 10:07:09 UTC 2010


On Wed, Dec 01, 2010 at 02:58:48PM -0500, Stephen Gallagher wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> On 11/15/2010 08:49 AM, Sumit Bose wrote:
> > Hi,
> > 
> > this series for patches add support for automatic Kerberos ticket
> > renewal, see also trac ticket #369.
> > 
> > There are several things I like to discuss:
> > - in the ticket a separate process which should handle the renewal was
> >   mentioned. Currently the patches just create a timed task in the krb5
> >   provider because I think most of the typically uses cases do not
> >   justify to overhead we create with a separate process. But I'm open
> >   for other arguments.
> > - I have added option to request TGT with a specific lifetime/renewal
> >   time. The corresponding option in krb5.conf have a trailing letter
> >   indicating the time unit. I have copied this behaviour to help
> >   migrations although we typically use only seconds without a unit in
> >   sssd.conf. Is this a good idea or shall I change it to seconds or do
> >   we want to support both formats.
> > - Currently everything is held in RAM and after a restart nothing is
> >   renewed automatically. I plan to send a new patch which checks all
> >   ccfiles we have in the cache and if renewal is possible it adds them
> >   to the list at startup. I think this approach makes more sense than
> >   writing the list of renewable ticket to disk. Do you agree?
> 
> 
> Patch 0001: Ack
> Patch 0002: Ack
> Patch 0003: Ack
> Patch 0004: Nack. Please check spelling/grammar in the manpage entry.
> ("possible" is misspelled and "hour" should be plural)
> Also, please use something more descriptive than "dummy". That moniker
> should be reserved for actual dummy variables whose contents we don't
> care about.

done

> I'm wondering whether it makes sense to have the renewable length as an
> environment variable. I think it might be wiser to pass that as an
> argument. There may be times in the future where we want to
> differentiate renewable length by group or other distinguishing feature
> of an account.

opened ticket #697 to track this

> 
> Patch 0005: Nack for most of the same reasons as patch 0004
> Patch 0006: Ack
> Patch 0007: Nack
> This looks wrong to me:
> SAFEALIGN_COPY_INT32(&int64_value, buf+p, NULL);
> If I'm misreading it and your intention actually is to ignore a leading
> 32-bit value here, please add a comment explaining it.

sorry, this was a copy-and-paste error, the following
SAFEALIGN_COPY_INT64 is only needed

> 
> Also, I'm concerned about casting a 64-bit value to time_t (since time_t
> is "long int" on most platforms, which means only 32-bits on i386).
> Probably we want to add a routine to check the range and clamp it down
> to time_t max if it's too large. Also, instead of using int64_value,
> please use different variables with clearer names.

done

> 
> Patch 0008: Nack.
> 
> This might be a matter of optimizing too early, but why do we have a
> renew interval instead of just setting the tevent timer based on the
> next ticket that's due to be renewed? Is there any advantage to
> maintaining the hash table of entries? I think it would make more sense
> to just create timed events, rather than adding TGTs to a hash table.
> Then we don't have to iterate across all of them if only one is due for
> update.

we discussed this on irc and agreed that the current approach has its
values too, e.g. helps to reduce the number of requests to the KDC. So
we keep it in the current state.

> 
> Also, if we're offline when the renewal period hits, we should add a
> renewal action to the list of going_online callbacks, rather than just
> rescheduling and hoping for the best. This way, if we happen to go
> online two minutes before the expire time would be up, they'll get the
> renewal in on time.

done

> 
> So to summarize: my view would be that when we get a TGT, we should just
> create a timed event that would fire at max_renewal_time/2. If this
> renewal succeeds, reschedule the event for new_max_renewal_time/2, and
> if we're offline, add it to going_online callbacks so it will be tried
> as soon as we go online after that.
> 
> Patch 0009: Nack.
> str[strlen(str)] is always going to be '\0', unless I miss my guess. So
> I don't think this if statement does what you think it does.

ah, sorry, forgot the -1, also fixed the 'unused variable' warning.

Thanks for the review, new versions 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/
> 
> iEYEARECAAYFAkz2qPgACgkQeiVVYja6o6MtRQCfRU/v+uRS4xC/5v0wPonLxiZ5
> H9cAnAvdkHgkkE1WyMRp+Wzz3NdHP79M
> =fZDQ
> -----END PGP SIGNATURE-----
> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://fedorahosted.org/mailman/listinfo/sssd-devel
-------------- next part --------------
From aef76874994bb741c0a98297b485593f5f89c156 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 9 Nov 2010 13:48:40 +0100
Subject: [PATCH 1/9] Send authtok_type to krb5_child

---
 src/providers/krb5/krb5_child.c         |    2 ++
 src/providers/krb5/krb5_child_handler.c |    6 ++++--
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index fda3589..5a5281a 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -916,6 +916,7 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd,
         if (kr->keytab == NULL) return ENOMEM;
         p += len;
 
+        SAFEALIGN_COPY_UINT32_CHECK(&pd->authtok_type, buf + p, size, &p);
         SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
         if ((p + len) > size) return EINVAL;
         pd->authtok = (uint8_t *)talloc_strndup(pd, (char *)(buf + p), len);
@@ -930,6 +931,7 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd,
     }
 
     if (pd->cmd == SSS_PAM_CHAUTHTOK) {
+        SAFEALIGN_COPY_UINT32_CHECK(&pd->newauthtok_type, buf + p, size, &p);
         SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
 
         if ((p + len) > size) return EINVAL;
diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
index e708c50..9da8a37 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -106,12 +106,12 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
     if (kr->pd->cmd == SSS_PAM_AUTHENTICATE ||
         kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ||
         kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
-        buf->size += 3*sizeof(uint32_t) + strlen(kr->ccname) + strlen(keytab) +
+        buf->size += 4*sizeof(uint32_t) + strlen(kr->ccname) + strlen(keytab) +
                      kr->pd->authtok_size;
     }
 
     if (kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
-        buf->size += sizeof(uint32_t) + kr->pd->newauthtok_size;
+        buf->size += 2*sizeof(uint32_t) + kr->pd->newauthtok_size;
     }
 
     if (kr->pd->cmd == SSS_PAM_ACCT_MGMT) {
@@ -145,12 +145,14 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
         SAFEALIGN_SET_UINT32(&buf->data[rp], strlen(keytab), &rp);
         safealign_memcpy(&buf->data[rp], keytab, strlen(keytab), &rp);
 
+        SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->pd->authtok_type, &rp);
         SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->pd->authtok_size, &rp);
         safealign_memcpy(&buf->data[rp], kr->pd->authtok,
                          kr->pd->authtok_size, &rp);
     }
 
     if (kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
+        SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->pd->newauthtok_type, &rp);
         SAFEALIGN_COPY_UINT32(&buf->data[rp], &kr->pd->newauthtok_size, &rp);
         safealign_memcpy(&buf->data[rp], kr->pd->newauthtok,
                          kr->pd->newauthtok_size, &rp);
-- 
1.7.3.2

-------------- next part --------------
From 10f16d66f50b6a944505a8e9560fd1790d8f75ad Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 8 Nov 2010 14:24:05 +0100
Subject: [PATCH 2/9] Add a renew task to krb5_child

---
 src/providers/krb5/krb5_child.c |   87 +++++++++++++++++++++++++++++++++++++++
 src/sss_client/sss_cli.h        |    8 +++-
 2 files changed, 94 insertions(+), 1 deletions(-)

diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 5a5281a..c4af471 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -863,6 +863,89 @@ static errno_t kuserok_child(int fd, struct krb5_req *kr)
     return ret;
 }
 
+static errno_t renew_tgt_child(int fd, struct krb5_req *kr)
+{
+    int ret;
+    int status = PAM_AUTHTOK_ERR;
+    int kerr;
+    char *ccname;
+    krb5_ccache ccache = NULL;
+
+    if (kr->pd->authtok_type != SSS_AUTHTOK_TYPE_CCFILE) {
+        DEBUG(1, ("Unsupported authtok type for TGT renewal [%d].\n",
+                  kr->pd->authtok_type));
+        goto done;
+    }
+
+    ccname = talloc_strndup(kr, (char *) kr->pd->authtok, kr->pd->authtok_size);
+    if (ccname == NULL) {
+        DEBUG(1, ("talloc_strndup failed.\n"));
+        goto done;
+    }
+
+    kerr = krb5_cc_resolve(kr->ctx, ccname, &ccache);
+    if (kerr != 0) {
+        KRB5_DEBUG(1, kerr);
+        goto done;
+    }
+
+    kerr = krb5_get_renewed_creds(kr->ctx, kr->creds, kr->princ, ccache, NULL);
+    if (kerr != 0) {
+        KRB5_DEBUG(1, kerr);
+        goto done;
+    }
+
+    if (kr->validate) {
+        kerr = validate_tgt(kr);
+        if (kerr != 0) {
+            KRB5_DEBUG(1, kerr);
+            goto done;
+        }
+
+    } else {
+        DEBUG(9, ("TGT validation is disabled.\n"));
+    }
+
+    if (kr->validate) {
+        /* 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. */
+        ret = become_user(kr->uid, kr->gid);
+        if (ret != EOK) {
+            DEBUG(1, ("become_user failed.\n"));
+            goto done;
+        }
+    }
+
+    kerr = krb5_cc_initialize(kr->ctx, ccache, kr->princ);
+    if (kerr != 0) {
+        KRB5_DEBUG(1, kerr);
+        goto done;
+    }
+
+    kerr = krb5_cc_store_cred(kr->ctx, ccache, kr->creds);
+    if (kerr != 0) {
+        KRB5_DEBUG(1, kerr);
+        goto done;
+    }
+
+    status = PAM_SUCCESS;
+
+done:
+    krb5_free_cred_contents(kr->ctx, kr->creds);
+
+    if (ccache != NULL) {
+        krb5_cc_close(kr->ctx, ccache);
+    }
+
+    ret = sendresponse(fd, 0, status, kr);
+    if (ret != EOK) {
+        DEBUG(1, ("sendresponse failed.\n"));
+    }
+
+    return ret;
+}
+
 static errno_t create_empty_ccache(int fd, struct krb5_req *kr)
 {
     int ret;
@@ -903,6 +986,7 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, struct pam_data *pd,
     p += len;
 
     if (pd->cmd == SSS_PAM_AUTHENTICATE ||
+        pd->cmd == SSS_CMD_RENEW ||
         pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM || pd->cmd == SSS_PAM_CHAUTHTOK) {
         SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
         if ((p + len ) > size) return EINVAL;
@@ -1017,6 +1101,9 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
         case SSS_PAM_ACCT_MGMT:
             kr->child_req = kuserok_child;
             break;
+        case SSS_CMD_RENEW:
+            kr->child_req = renew_tgt_child;
+            break;
         default:
             DEBUG(1, ("PAM command [%d] not supported.\n", kr->pd->cmd));
             kerr = EINVAL;
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index b052106..7f579da 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -181,7 +181,9 @@ enum sss_cli_command {
                                         * operation where the PAM_PRELIM_CHECK
                                         * flag is set, see pam_sm_chauthtok(3)
                                         * for details */
-
+    SSS_CMD_RENEW            = 0x00F8, /**< Renew a credential with a limited
+                                        * lifetime, e.g. a Kerberos Ticket
+                                        * Granting Ticket (TGT) */
 };
 
 /**
@@ -228,6 +230,10 @@ enum sss_authtok_type {
     SSS_AUTHTOK_TYPE_PASSWORD =  0x0001, /**< Authentication token is a
                                           * password, it may or may no contain
                                           * a trailing \\0 */
+    SSS_AUTHTOK_TYPE_CCFILE =    0x0002, /**< Authentication token is a path to
+                                          * a Kerberos credential cache file,
+                                          * it may or may no contain
+                                          * a trailing \\0 */
 };
 
 /**
-- 
1.7.3.2

-------------- next part --------------
From 6303f672f62e391bc74619cf9ef3231d8ab921de Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 8 Nov 2010 15:00:19 +0100
Subject: [PATCH 3/9] Check authtok type for krb5 auth and chpass

---
 src/providers/krb5/krb5_child.c |   12 ++++++++++++
 1 files changed, 12 insertions(+), 0 deletions(-)

diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index c4af471..0e55560 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -620,6 +620,12 @@ static errno_t changepw_child(int fd, struct krb5_req *kr)
     char *changepw_princ = NULL;
     krb5_prompter_fct prompter = sss_krb5_prompter;
 
+    if (kr->pd->authtok_type != SSS_AUTHTOK_TYPE_PASSWORD) {
+        pam_status = PAM_CRED_INSUFFICIENT;
+        kerr = KRB5KRB_ERR_GENERIC;
+        goto sendresponse;
+    }
+
     pass_str = talloc_strndup(kr, (const char *) kr->pd->authtok,
                               kr->pd->authtok_size);
     if (pass_str == NULL) {
@@ -760,6 +766,12 @@ static errno_t tgt_req_child(int fd, struct krb5_req *kr)
     char *changepw_princ = NULL;
     int pam_status = PAM_SYSTEM_ERR;
 
+    if (kr->pd->authtok_type != SSS_AUTHTOK_TYPE_PASSWORD) {
+        pam_status = PAM_CRED_INSUFFICIENT;
+        kerr = KRB5KRB_ERR_GENERIC;
+        goto sendresponse;
+    }
+
     pass_str = talloc_strndup(kr, (const char *) kr->pd->authtok,
                               kr->pd->authtok_size);
     if (pass_str == NULL) {
-- 
1.7.3.2

-------------- next part --------------
From 7560de25df6722b625e0d89dfddf11b07b715487 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 8 Nov 2010 16:00:57 +0100
Subject: [PATCH 4/9] Add krb5_renewable_lifetime option

---
 Makefile.am                              |    1 +
 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                  |   32 ++++++++++++++++++++++++++++++
 src/providers/ipa/ipa_common.c           |    3 +-
 src/providers/ipa/ipa_common.h           |    2 +-
 src/providers/krb5/krb5_child.c          |   18 +++++++++++++++-
 src/providers/krb5/krb5_common.c         |   24 +++++++++++++++++++++-
 src/providers/krb5/krb5_common.h         |    2 +
 10 files changed, 86 insertions(+), 7 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 6d91cf6..4a8fd59 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -531,6 +531,7 @@ krb5_utils_tests_CFLAGS = \
 krb5_utils_tests_LDADD = \
     $(SSSD_LIBS)\
     $(CARES_LIBS) \
+    $(KRB5_LIBS) \
     $(CHECK_LIBS) \
     libsss_test_common.la
 
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index 1f54b47..bb7e424 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -104,6 +104,7 @@ option_strings = {
     '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"),
+    'krb5_renewable_lifetime' : _("Renewable lifetime of the TGT"),
 
     # [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 39db49d..b63e40b 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -549,7 +549,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
              'krb5_keytab',
              'krb5_validate',
              'krb5_store_password_if_offline',
-             'krb5_auth_timeout'])
+             'krb5_auth_timeout',
+             'krb5_renewable_lifetime'])
 
         options = domain.list_options()
 
@@ -723,7 +724,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'krb5_keytab',
             'krb5_validate',
             'krb5_store_password_if_offline',
-            'krb5_auth_timeout']
+            'krb5_auth_timeout',
+            'krb5_renewable_lifetime']
 
         self.assertTrue(type(options) == dict,
                         "Options should be a dictionary")
@@ -871,7 +873,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
              'krb5_keytab',
              'krb5_validate',
              'krb5_store_password_if_offline',
-             'krb5_auth_timeout'])
+             'krb5_auth_timeout',
+             'krb5_renewable_lifetime'])
 
         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 0c0aa42..c3cc5bb 100644
--- a/src/config/etc/sssd.api.d/sssd-krb5.conf
+++ b/src/config/etc/sssd.api.d/sssd-krb5.conf
@@ -11,6 +11,7 @@ krb5_ccname_template = str, None, false
 krb5_keytab = str, None, false
 krb5_validate = bool, None, false
 krb5_store_password_if_offline = bool, None, false
+krb5_renewable_lifetime = str, None, false
 
 [provider/krb5/chpass]
 
diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml
index f5c8ad0..0f0c8a2 100644
--- a/src/man/sssd-krb5.5.xml
+++ b/src/man/sssd-krb5.5.xml
@@ -268,6 +268,38 @@
                     </listitem>
                 </varlistentry>
 
+                <varlistentry>
+                    <term>krb5_renewable_lifetime (string)</term>
+                    <listitem>
+                        <para>
+                            Request a renewable ticket with a with a total
+                            lifetime given by an integer immediately followed
+                            by one of the following delimiters:
+                        </para>
+                        <para>
+                            <emphasis>s</emphasis> seconds
+                        </para>
+                        <para>
+                            <emphasis>m</emphasis> minutes
+                        </para>
+                        <para>
+                            <emphasis>h</emphasis> hours
+                        </para>
+                        <para>
+                            <emphasis>d</emphasis> days.
+                        </para>
+                        <para>
+                            Please note that it is not possible to mix units.
+                            If you want to set the renewable lifetime to one
+                            and a half hours please use '90m' instead of
+                            '1h30m'.
+                        </para>
+                        <para>
+                            Default: not set, i.e. the TGT is not renewable
+                        </para>
+                    </listitem>
+                </varlistentry>
+
             </variablelist>
         </para>
     </refsect1>
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index a7f4494..149a352 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -139,7 +139,8 @@ struct dp_option ipa_def_krb5_opts[] = {
     { "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_store_password_if_offline", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }
+    { "krb5_store_password_if_offline", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+    { "krb5_renewable_lifetime", 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 ef1cac2..33b325c 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/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 0e55560..2a2ed9b 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -1084,6 +1084,8 @@ static int krb5_cleanup(void *ptr)
 static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
 {
     krb5_error_code kerr = 0;
+    char *lifetime_str;
+    krb5_deltat lifetime;
 
     kr->krb5_ctx = talloc_zero(kr, struct krb5_child_ctx);
     if (kr->krb5_ctx == NULL) {
@@ -1162,9 +1164,23 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
         goto failed;
     }
 
+    lifetime_str = getenv(SSSD_KRB5_RENEWABLE_LIFETIME);
+    if (lifetime_str == NULL) {
+        DEBUG(7, ("Cannot read [%s] from environment.\n",
+                  SSSD_KRB5_RENEWABLE_LIFETIME));
+    } else {
+        kerr = krb5_string_to_deltat(lifetime_str, &lifetime);
+        if (kerr != 0) {
+            DEBUG(1, ("krb5_string_to_deltat failed for [%s].\n",
+                      lifetime_str));
+            KRB5_DEBUG(1, kerr);
+            goto failed;
+        }
+        krb5_get_init_creds_opt_set_renew_life(kr->options, lifetime);
+    }
+
 /* TODO: set options, e.g.
  *  krb5_get_init_creds_opt_set_tkt_life
- *  krb5_get_init_creds_opt_set_renew_life
  *  krb5_get_init_creds_opt_set_forwardable
  *  krb5_get_init_creds_opt_set_proxiable
  *  krb5_get_init_creds_opt_set_etype_list
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index bf47dcc..1a62bbe 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -40,7 +40,8 @@ struct dp_option default_krb5_opts[] = {
     { "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_store_password_if_offline", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }
+    { "krb5_store_password_if_offline", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+    { "krb5_renewable_lifetime", DP_OPT_STRING, NULL_STRING, NULL_STRING }
 };
 
 errno_t check_and_export_options(struct dp_option *opts,
@@ -49,6 +50,8 @@ errno_t check_and_export_options(struct dp_option *opts,
     int ret;
     const char *realm;
     const char *dummy;
+    char *str;
+    krb5_deltat lifetime;
 
     realm = dp_opt_get_cstring(opts, KRB5_REALM);
     if (realm == NULL) {
@@ -66,6 +69,25 @@ errno_t check_and_export_options(struct dp_option *opts,
                   SSSD_KRB5_REALM));
     }
 
+    str = dp_opt_get_string(opts, KRB5_RENEWABLE_LIFETIME);
+    if (str == NULL) {
+        DEBUG(5, ("No renewable lifetime configured.\n"));
+    } else {
+        ret = krb5_string_to_deltat(str, &lifetime);
+        if (ret != 0) {
+            DEBUG(1, ("Invalid value [%s] for krb5_renewable_lifetime.\n",
+                      str));
+            return EINVAL;
+        }
+
+        ret = setenv(SSSD_KRB5_RENEWABLE_LIFETIME, str, 1);
+        if (ret != EOK) {
+            DEBUG(2, ("setenv [%s] failed.\n",
+                      SSSD_KRB5_RENEWABLE_LIFETIME));
+            return ret;
+        }
+    }
+
     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 139ce40..15ef437 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -35,6 +35,7 @@
 
 #define SSSD_KRB5_KDC "SSSD_KRB5_KDC"
 #define SSSD_KRB5_REALM "SSSD_KRB5_REALM"
+#define SSSD_KRB5_RENEWABLE_LIFETIME "SSSD_KRB5_RENEWABLE_LIFETIME"
 
 #define KDCINFO_TMPL PUBCONF_PATH"/kdcinfo.%s"
 #define KPASSWDINFO_TMPL PUBCONF_PATH"/kpasswdinfo.%s"
@@ -52,6 +53,7 @@ enum krb5_opts {
     KRB5_VALIDATE,
     KRB5_KPASSWD,
     KRB5_STORE_PASSWORD_IF_OFFLINE,
+    KRB5_RENEWABLE_LIFETIME,
 
     KRB5_OPTS
 };
-- 
1.7.3.2

-------------- next part --------------
From 1a92b35d300b927126fb4853bf9db2e44fe0d69f Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 8 Nov 2010 18:25:56 +0100
Subject: [PATCH 5/9] Add krb5_lifetime option

---
 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                  |   32 ++++++++++++++++++++++++++++++
 src/providers/ipa/ipa_common.c           |    3 +-
 src/providers/ipa/ipa_common.h           |    2 +-
 src/providers/krb5/krb5_child.c          |   16 ++++++++++++++-
 src/providers/krb5/krb5_common.c         |   22 +++++++++++++++++++-
 src/providers/krb5/krb5_common.h         |    2 +
 9 files changed, 81 insertions(+), 7 deletions(-)

diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index bb7e424..1286e54 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -105,6 +105,7 @@ option_strings = {
     'krb5_validate' : _("Enable credential validation"),
     '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"),
 
     # [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 b63e40b..3d57108 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -550,7 +550,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
              'krb5_validate',
              'krb5_store_password_if_offline',
              'krb5_auth_timeout',
-             'krb5_renewable_lifetime'])
+             'krb5_renewable_lifetime',
+             'krb5_lifetime'])
 
         options = domain.list_options()
 
@@ -725,7 +726,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'krb5_validate',
             'krb5_store_password_if_offline',
             'krb5_auth_timeout',
-            'krb5_renewable_lifetime']
+            'krb5_renewable_lifetime',
+            'krb5_lifetime']
 
         self.assertTrue(type(options) == dict,
                         "Options should be a dictionary")
@@ -874,7 +876,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
              'krb5_validate',
              'krb5_store_password_if_offline',
              'krb5_auth_timeout',
-             'krb5_renewable_lifetime'])
+             'krb5_renewable_lifetime',
+             'krb5_lifetime'])
 
         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 c3cc5bb..f824cfb 100644
--- a/src/config/etc/sssd.api.d/sssd-krb5.conf
+++ b/src/config/etc/sssd.api.d/sssd-krb5.conf
@@ -12,6 +12,7 @@ krb5_keytab = str, None, false
 krb5_validate = bool, None, false
 krb5_store_password_if_offline = bool, None, false
 krb5_renewable_lifetime = str, None, false
+krb5_lifetime = str, None, false
 
 [provider/krb5/chpass]
 
diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml
index 0f0c8a2..ff5d0a6 100644
--- a/src/man/sssd-krb5.5.xml
+++ b/src/man/sssd-krb5.5.xml
@@ -300,6 +300,38 @@
                     </listitem>
                 </varlistentry>
 
+                <varlistentry>
+                    <term>krb5_lifetime (string)</term>
+                    <listitem>
+                        <para>
+                            Request ticket with a with a lifetime given by an
+                            integer immediately followed by one of the following
+                            delimiters:
+                        </para>
+                        <para>
+                            <emphasis>s</emphasis> seconds
+                        </para>
+                        <para>
+                            <emphasis>m</emphasis> minutes
+                        </para>
+                        <para>
+                            <emphasis>h</emphasis> hours
+                        </para>
+                        <para>
+                            <emphasis>d</emphasis> days.
+                        </para>
+                        <para>
+                            Please note that it is not possible to mix units.
+                            If you want to set the lifetime to one and a half
+                            hours please use '90m' instead of '1h30m'.
+                        </para>
+                        <para>
+                            Default: not set, i.e. the default ticket lifetime
+                            configured on the KDC.
+                        </para>
+                    </listitem>
+                </varlistentry>
+
             </variablelist>
         </para>
     </refsect1>
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 149a352..545ddc9 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -140,7 +140,8 @@ struct dp_option ipa_def_krb5_opts[] = {
     { "krb5_validate", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
     { "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_renewable_lifetime", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "krb5_lifetime", 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 33b325c..be0f361 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 10
+#define IPA_KRB5_OPTS_TEST 11
 
 enum ipa_basic_opt {
     IPA_DOMAIN = 0,
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 2a2ed9b..f29869b 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -1179,8 +1179,22 @@ static int krb5_child_setup(struct krb5_req *kr, uint32_t offline)
         krb5_get_init_creds_opt_set_renew_life(kr->options, lifetime);
     }
 
+    lifetime_str = getenv(SSSD_KRB5_LIFETIME);
+    if (lifetime_str == NULL) {
+        DEBUG(7, ("Cannot read [%s] from environment.\n",
+                  SSSD_KRB5_LIFETIME));
+    } else {
+        kerr = krb5_string_to_deltat(lifetime_str, &lifetime);
+        if (kerr != 0) {
+            DEBUG(1, ("krb5_string_to_deltat failed for [%s].\n",
+                      lifetime_str));
+            KRB5_DEBUG(1, kerr);
+            goto failed;
+        }
+        krb5_get_init_creds_opt_set_tkt_life(kr->options, lifetime);
+    }
+
 /* TODO: set options, e.g.
- *  krb5_get_init_creds_opt_set_tkt_life
  *  krb5_get_init_creds_opt_set_forwardable
  *  krb5_get_init_creds_opt_set_proxiable
  *  krb5_get_init_creds_opt_set_etype_list
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 1a62bbe..7ee4d09 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -41,7 +41,8 @@ struct dp_option default_krb5_opts[] = {
     { "krb5_validate", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "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_renewable_lifetime", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "krb5_lifetime", DP_OPT_STRING, NULL_STRING, NULL_STRING }
 };
 
 errno_t check_and_export_options(struct dp_option *opts,
@@ -88,6 +89,25 @@ errno_t check_and_export_options(struct dp_option *opts,
         }
     }
 
+    str = dp_opt_get_string(opts, KRB5_LIFETIME);
+    if (str == NULL) {
+        DEBUG(5, ("No TGT lifetime configured.\n"));
+    } else {
+        ret = krb5_string_to_deltat(str, &lifetime);
+        if (ret != 0) {
+            DEBUG(1, ("Invalid value [%s] for krb5_lifetime.\n",
+                      str));
+            return EINVAL;
+        }
+
+        ret = setenv(SSSD_KRB5_LIFETIME, str, 1);
+        if (ret != EOK) {
+            DEBUG(2, ("setenv [%s] failed.\n",
+                      SSSD_KRB5_LIFETIME));
+            return ret;
+        }
+    }
+
     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 15ef437..01d2dbf 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -36,6 +36,7 @@
 #define SSSD_KRB5_KDC "SSSD_KRB5_KDC"
 #define SSSD_KRB5_REALM "SSSD_KRB5_REALM"
 #define SSSD_KRB5_RENEWABLE_LIFETIME "SSSD_KRB5_RENEWABLE_LIFETIME"
+#define SSSD_KRB5_LIFETIME "SSSD_KRB5_LIFETIME"
 
 #define KDCINFO_TMPL PUBCONF_PATH"/kdcinfo.%s"
 #define KPASSWDINFO_TMPL PUBCONF_PATH"/kpasswdinfo.%s"
@@ -54,6 +55,7 @@ enum krb5_opts {
     KRB5_KPASSWD,
     KRB5_STORE_PASSWORD_IF_OFFLINE,
     KRB5_RENEWABLE_LIFETIME,
+    KRB5_LIFETIME,
 
     KRB5_OPTS
 };
-- 
1.7.3.2

-------------- next part --------------
From 19100004b96ec64a6a2607106c35d8685b7355e6 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 9 Nov 2010 12:53:04 +0100
Subject: [PATCH 6/9] Add support for server-side pam response messages

---
 src/providers/data_provider.h  |    8 ++++++++
 src/responder/pam/pamsrv_cmd.c |    2 ++
 2 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/src/providers/data_provider.h b/src/providers/data_provider.h
index 6fe3b3b..e55b4ab 100644
--- a/src/providers/data_provider.h
+++ b/src/providers/data_provider.h
@@ -193,6 +193,14 @@ struct pam_data {
 };
 
 /* from dp_auth_util.c */
+#define SSS_SERVER_INFO 0x80000000
+
+#define SSS_KRB5_INFO 0x40000000
+#define SSS_LDAP_INFO 0x20000000
+#define SSS_PROXY_INFO 0x10000000
+
+#define SSS_KRB5_INFO_TGT_LIFETIME (SSS_SERVER_INFO|SSS_KRB5_INFO|0x01)
+
 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);
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index 25031e1..27a6195 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -373,6 +373,8 @@ static errno_t filter_responses(struct response_data *resp_list,
                 default:
                     DEBUG(7, ("User info type [%d] not filtered.\n"));
             }
+        } else if (resp->type & SSS_SERVER_INFO) {
+            resp->do_not_send_to_client = true;
         }
 
         resp = resp->next;
-- 
1.7.3.2

-------------- next part --------------
From 2ad26b5202d6819b614e707aaf6ae0e89e35c041 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 9 Nov 2010 13:11:52 +0100
Subject: [PATCH 7/9] krb5_child returns TGT lifetime

---
 src/providers/krb5/krb5_auth.c   |   21 +++++++++++++++++++++
 src/providers/krb5/krb5_child.c  |   29 +++++++++++++++++++++++++++++
 src/providers/krb5/krb5_common.h |    7 +++++++
 src/util/util.h                  |    6 ++++++
 4 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 9dc7a2c..974e768 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -39,6 +39,9 @@
 #include "providers/krb5/krb5_auth.h"
 #include "providers/krb5/krb5_utils.h"
 
+#define TIME_T_MAX LONG_MAX
+#define int64_to_time_t(val) ((time_t)((val) < TIME_T_MAX ? val : TIME_T_MAX))
+
 static errno_t safe_remove_old_ccache_file(const char *old_ccache_file,
                                            const char *new_ccache_file)
 {
@@ -688,6 +691,10 @@ static void krb5_child_done(struct tevent_req *subreq)
     int32_t msg_status;
     int32_t msg_type;
     int32_t msg_len;
+    int64_t time_data;
+    struct tgt_times tgtt;
+
+    memset(&tgtt, 0, sizeof(tgtt));
 
     ret = handle_child_recv(subreq, pd, &buf, &len);
     talloc_zfree(subreq);
@@ -751,6 +758,20 @@ static void krb5_child_done(struct tevent_req *subreq)
             }
         }
 
+        if (msg_type == SSS_KRB5_INFO_TGT_LIFETIME &&
+            msg_len == 4*sizeof(int64_t)) {
+            SAFEALIGN_COPY_INT64(&time_data, buf+p, NULL);
+            tgtt.authtime = int64_to_time_t(time_data);
+            SAFEALIGN_COPY_INT64(&time_data, buf+p+sizeof(int64_t), NULL);
+            tgtt.starttime = int64_to_time_t(time_data);
+            SAFEALIGN_COPY_INT64(&time_data, buf+p+2*sizeof(int64_t), NULL);
+            tgtt.endtime = int64_to_time_t(time_data);
+            SAFEALIGN_COPY_INT64(&time_data, buf+p+3*sizeof(int64_t), NULL);
+            tgtt.renew_till = int64_to_time_t(time_data);
+            DEBUG(7, ("TGT times are [%d][%d][%d][%d].\n", tgtt.authtime,
+                      tgtt.starttime, tgtt.endtime, tgtt.renew_till));
+        }
+
         ret = pam_add_response(pd, msg_type, msg_len, &buf[p]);
         if (ret != EOK) {
             /* This is not a fatal error */
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index f29869b..c12478f 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -456,6 +456,25 @@ static errno_t sendresponse(int fd, krb5_error_code kerr, int pam_status,
     return EOK;
 }
 
+static errno_t add_ticket_times_to_response(struct krb5_req *kr)
+{
+    int ret;
+    int64_t t[4];
+
+    t[0] = (int64_t) kr->creds->times.authtime;
+    t[1] = (int64_t) kr->creds->times.starttime;
+    t[2] = (int64_t) kr->creds->times.endtime;
+    t[3] = (int64_t) kr->creds->times.renew_till;
+
+    ret = pam_add_response(kr->pd, SSS_KRB5_INFO_TGT_LIFETIME,
+                           4*sizeof(int64_t), (uint8_t *) t);
+    if (ret != EOK) {
+        DEBUG(1, ("pack_response_packet failed.\n"));
+    }
+
+    return ret;
+}
+
 static krb5_error_code validate_tgt(struct krb5_req *kr)
 {
     krb5_error_code kerr;
@@ -595,6 +614,11 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
         goto done;
     }
 
+    ret = add_ticket_times_to_response(kr);
+    if (ret != EOK) {
+        DEBUG(1, ("add_ticket_times_to_response failed.\n"));
+    }
+
     kerr = 0;
 
 done:
@@ -941,6 +965,11 @@ static errno_t renew_tgt_child(int fd, struct krb5_req *kr)
         goto done;
     }
 
+    ret = add_ticket_times_to_response(kr);
+    if (ret != EOK) {
+        DEBUG(1, ("add_ticket_times_to_response failed.\n"));
+    }
+
     status = PAM_SUCCESS;
 
 done:
diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h
index 01d2dbf..68e4426 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -62,6 +62,13 @@ enum krb5_opts {
 
 typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type;
 
+struct tgt_times {
+    time_t authtime;
+    time_t starttime;
+    time_t endtime;
+    time_t renew_till;
+};
+
 struct krb5_service {
     char *name;
     char *address;
diff --git a/src/util/util.h b/src/util/util.h
index e480694..12d3ff0 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -185,6 +185,12 @@ safealign_memcpy(void *dest, const void *src, size_t n, size_t *counter)
     safealign_memcpy(dest, &CV_MACRO_val, sizeof(type), pctr); \
 } while(0)
 
+#define SAFEALIGN_COPY_INT64(dest, src, pctr) \
+    safealign_memcpy(dest, src, sizeof(int64_t), pctr)
+
+#define SAFEALIGN_SET_INT64(dest, value, pctr) \
+    SAFEALIGN_SET_VALUE(dest, value, int64_t, pctr)
+
 #define SAFEALIGN_COPY_UINT32(dest, src, pctr) \
     safealign_memcpy(dest, src, sizeof(uint32_t), pctr)
 
-- 
1.7.3.2

-------------- next part --------------
From 47f9461232a0cd55e643cf4241dd6b0a9fd50578 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      |  367 ++++++++++++++++++++++++++++++
 14 files changed, 438 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..7bbf227
--- /dev/null
+++ b/src/providers/krb5/krb5_renew_tgt.c
@@ -0,0 +1,367 @@
+/*
+    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;
+};
+
+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_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)) {
+        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);
+        return;
+    } 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;
+
+
+    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

-------------- next part --------------
From 7b09e77e7692dfab480cb0935e4cc4ec6ac4e979 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Thu, 18 Nov 2010 17:40:57 +0100
Subject: [PATCH 9/9] Allow krb5 lifetime values without a unit

---
 src/man/sssd-krb5.5.xml          |    8 +++
 src/providers/krb5/krb5_common.c |  101 ++++++++++++++++++++++++-------------
 2 files changed, 73 insertions(+), 36 deletions(-)

diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml
index a499d75..d1341d9 100644
--- a/src/man/sssd-krb5.5.xml
+++ b/src/man/sssd-krb5.5.xml
@@ -289,6 +289,10 @@
                             <emphasis>d</emphasis> days.
                         </para>
                         <para>
+                            If there is no delimiter <emphasis>s</emphasis> is
+                            assumed.
+                        </para>
+                        <para>
                             Please note that it is not possible to mix units.
                             If you want to set the renewable lifetime to one
                             and a half hours please use '90m' instead of
@@ -321,6 +325,10 @@
                             <emphasis>d</emphasis> days.
                         </para>
                         <para>
+                            If there is no delimiter <emphasis>s</emphasis> is
+                            assumed.
+                        </para>
+                        <para>
                             Please note that it is not possible to mix units.
                             If you want to set the lifetime to one and a half
                             hours please use '90m' instead of '1h30m'.
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index cdc8437..43535c1 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -46,14 +46,65 @@ struct dp_option default_krb5_opts[] = {
     { "krb5_renew_interval", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER }
 };
 
+errno_t check_and_export_lifetime(struct dp_option *opts, const int opt_id,
+                                  const char *env_name)
+{
+    int ret;
+    char *str;
+    krb5_deltat lifetime;
+    bool free_str = false;
+
+    str = dp_opt_get_string(opts, opt_id);
+    if (str == NULL || *str == '\0') {
+        DEBUG(5, ("No lifetime configured.\n"));
+        return EOK;
+    }
+
+    if (isdigit(str[strlen(str)-1])) {
+        str = talloc_asprintf(opts, "%ss", str);
+        if (str == NULL) {
+            DEBUG(1, ("talloc_asprintf failed\n"));
+            return ENOMEM;
+        }
+        free_str = true;
+
+        ret = dp_opt_set_string(opts, opt_id, str);
+        if (ret != EOK) {
+            DEBUG(1, ("dp_opt_set_string failed\n"));
+            goto done;
+        }
+    }
+
+    ret = krb5_string_to_deltat(str, &lifetime);
+    if (ret != 0) {
+        DEBUG(1, ("Invalid value [%s] for a lifetime.\n", str));
+        ret = EINVAL;
+        goto done;
+    }
+
+    ret = setenv(env_name, str, 1);
+    if (ret != EOK) {
+        DEBUG(2, ("setenv [%s] failed.\n", env_name));
+        goto done;
+    }
+
+    ret = EOK;
+
+done:
+    if (free_str) {
+        talloc_free(str);
+    }
+
+    return ret;
+}
+
+
 errno_t check_and_export_options(struct dp_option *opts,
                                  struct sss_domain_info *dom)
 {
     int ret;
     const char *realm;
     const char *dummy;
-    char *str;
-    krb5_deltat lifetime;
 
     realm = dp_opt_get_cstring(opts, KRB5_REALM);
     if (realm == NULL) {
@@ -71,42 +122,20 @@ errno_t check_and_export_options(struct dp_option *opts,
                   SSSD_KRB5_REALM));
     }
 
-    str = dp_opt_get_string(opts, KRB5_RENEWABLE_LIFETIME);
-    if (str == NULL) {
-        DEBUG(5, ("No renewable lifetime configured.\n"));
-    } else {
-        ret = krb5_string_to_deltat(str, &lifetime);
-        if (ret != 0) {
-            DEBUG(1, ("Invalid value [%s] for krb5_renewable_lifetime.\n",
-                      str));
-            return EINVAL;
-        }
-
-        ret = setenv(SSSD_KRB5_RENEWABLE_LIFETIME, str, 1);
-        if (ret != EOK) {
-            DEBUG(2, ("setenv [%s] failed.\n",
-                      SSSD_KRB5_RENEWABLE_LIFETIME));
-            return ret;
-        }
+    ret = check_and_export_lifetime(opts, KRB5_RENEWABLE_LIFETIME,
+                                    SSSD_KRB5_RENEWABLE_LIFETIME);
+    if (ret != EOK) {
+        DEBUG(1, ("Failed to check value of krb5_renewable_lifetime. [%d][%s]\n",
+                  ret, strerror(ret)));
+        return ret;
     }
 
-    str = dp_opt_get_string(opts, KRB5_LIFETIME);
-    if (str == NULL) {
-        DEBUG(5, ("No TGT lifetime configured.\n"));
-    } else {
-        ret = krb5_string_to_deltat(str, &lifetime);
-        if (ret != 0) {
-            DEBUG(1, ("Invalid value [%s] for krb5_lifetime.\n",
-                      str));
-            return EINVAL;
-        }
-
-        ret = setenv(SSSD_KRB5_LIFETIME, str, 1);
-        if (ret != EOK) {
-            DEBUG(2, ("setenv [%s] failed.\n",
-                      SSSD_KRB5_LIFETIME));
-            return ret;
-        }
+    ret = check_and_export_lifetime(opts, KRB5_LIFETIME,
+                                    SSSD_KRB5_LIFETIME);
+    if (ret != EOK) {
+        DEBUG(1, ("Failed to check value of krb5_lifetime. [%d][%s]\n",
+                  ret, strerror(ret)));
+        return ret;
     }
 
     dummy = dp_opt_get_cstring(opts, KRB5_KDC);
-- 
1.7.3.2



More information about the sssd-devel mailing list