[SSSD] [PATCH] Try to renew Kerberos credentials

Sumit Bose sbose at redhat.com
Mon Dec 7 14:09:14 UTC 2009


On Mon, Dec 07, 2009 at 08:06:07AM -0500, Stephen Gallagher wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> On 12/04/2009 05:15 AM, Sumit Bose wrote:
> > Hi,
> > 
> > this patch should fix #299. Before going offline it is checked if GSSAPI
> > is used and the TGT is expired.
> > 
> > bye,
> > Sumit
> > 
> 
> Nack.
> 
> In sdap_check_gssapi_reconnect(), I think you should use server_name and
> server_principal instead of "service". On first readthrough I was
> thinking that this was supposed to be the LDAP service name, and I
> thought it was wrong, when really it's the KDC service name.
> 
> 
> This is an unsafe use of errno. Please store it. (fprintf or any future
> function we use in the DEBUG macro may alter errno):
> ret = gethostname(hostname, sizeof(hostname)-1);
> if (ret == -1) {
>   DEBUG(1, ("gethostname failed [%d][%s].\n", errno, strerror(errno)));
>   goto done;
> }
> 
> I think the DEBUG statement for the client principal name should be a
> little lower than level 9. Perhaps 6 or 7. (9 is more or less reserved
> for really low-level, high volume messages like the D-BUS dispatches).
> If we needed to check this value, that's too noisy a level for it. Same
> comment for the TGT end time.
> 

ok, new version 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.10 (GNU/Linux)
> Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/
> 
> iEYEARECAAYFAksc/b8ACgkQeiVVYja6o6Pz5QCeLiD560xmCy9d4dRDMiSwvsWO
> KdEAoIWtDejWw6FHG36bjNsNcMYg/ZV9
> =o1bp
> -----END PGP SIGNATURE-----
> _______________________________________________
> sssd-devel mailing list
> sssd-devel at lists.fedorahosted.org
> https://fedorahosted.org/mailman/listinfo/sssd-devel
-------------- next part --------------
From 8cffc16a0fb1a0589b4a582cfffab0801dfac526 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Fri, 4 Dec 2009 11:04:34 +0100
Subject: [PATCH] Try to renew Kerberos credentials

When using GSSAPI we need a valid service ticket to talk to the LDAP
server. If the ticket is expired the LDAP client returns with 'Can't
contact LDAP server'. Currently we set the backend offline if this error
occurs although the server is still available. This patch checks if the
TGT is expired and tries to renew the credentials before going offline.
---
 server/providers/ldap/ldap_common.c     |  139 +++++++++++++++++++++++++++++++
 server/providers/ldap/ldap_common.h     |    1 +
 server/providers/ldap/ldap_id.c         |   15 ++++
 server/providers/ldap/ldap_id_cleanup.c |   18 ++++-
 server/providers/ldap/ldap_id_enum.c    |   18 ++++-
 5 files changed, 189 insertions(+), 2 deletions(-)

diff --git a/server/providers/ldap/ldap_common.c b/server/providers/ldap/ldap_common.c
index d43d148..e4f3f6b 100644
--- a/server/providers/ldap/ldap_common.c
+++ b/server/providers/ldap/ldap_common.c
@@ -25,6 +25,8 @@
 #include "providers/ldap/ldap_common.h"
 #include "providers/fail_over.h"
 
+#include "util/sss_krb5.h"
+
 /* a fd the child process would log into */
 int ldap_child_debug_fd = -1;
 
@@ -305,6 +307,143 @@ void sdap_mark_offline(struct sdap_id_ctx *ctx)
     be_mark_offline(ctx->be);
 }
 
+bool sdap_check_gssapi_reconnect(struct sdap_id_ctx *ctx)
+{
+    int ret;
+    bool result = false;
+    const char *mech;
+    const char *realm;
+    char *ccname = NULL;
+    krb5_context context = NULL;
+    krb5_ccache ccache = NULL;
+    krb5_error_code krberr;
+    TALLOC_CTX *tmp_ctx = NULL;
+    krb5_creds mcred;
+    krb5_creds cred;
+    char *server_name = NULL;
+    char *client_princ_str = NULL;
+    char *full_princ = NULL;
+    krb5_principal client_principal = NULL;
+    krb5_principal server_principal = NULL;
+    char hostname[512];
+    int l_errno;
+
+
+    mech = dp_opt_get_string(ctx->opts->basic, SDAP_SASL_MECH);
+    if (mech == NULL || strcasecmp(mech, "GSSAPI") != 0) {
+        return false;
+    }
+
+    realm = dp_opt_get_string(ctx->opts->basic, SDAP_KRB5_REALM);
+    if (realm == NULL) {
+        DEBUG(3, ("Kerberos realm not available.\n"));
+        return false;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(1, ("talloc_new failed.\n"));
+        return false;
+    }
+
+    ccname = talloc_asprintf(tmp_ctx, "FILE:%s/ccache_%s", DB_PATH, realm);
+    if (ccname == NULL) {
+        DEBUG(1, ("talloc_asprintf failed.\n"));
+        goto done;
+    }
+
+    krberr = krb5_init_context(&context);
+    if (krberr) {
+        DEBUG(1, ("Failed to init kerberos context\n"));
+        goto done;
+    }
+
+    krberr = krb5_cc_resolve(context, ccname, &ccache);
+    if (krberr != 0) {
+        DEBUG(1, ("krb5_cc_resolve failed.\n"));
+        goto done;
+    }
+
+    server_name = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", realm, realm);
+    if (server_name == NULL) {
+        DEBUG(1, ("talloc_asprintf failed.\n"));
+        goto done;
+    }
+
+    krberr = krb5_parse_name(context, server_name, &server_principal);
+    if (krberr != 0) {
+        DEBUG(1, ("krb5_parse_name failed.\n"));
+        goto done;
+    }
+
+    client_princ_str = dp_opt_get_string(ctx->opts->basic, SDAP_SASL_AUTHID);
+    if (client_princ_str) {
+        if (!strchr(client_princ_str, '@')) {
+            full_princ = talloc_asprintf(tmp_ctx, "%s@%s", client_princ_str,
+                                         realm);
+        } else {
+            full_princ = talloc_strdup(tmp_ctx, client_princ_str);
+        }
+    } else {
+        ret = gethostname(hostname, sizeof(hostname)-1);
+        if (ret == -1) {
+            l_errno = errno;
+            DEBUG(1, ("gethostname failed [%d][%s].\n", l_errno,
+                                                        strerror(l_errno)));
+            goto done;
+        }
+        hostname[sizeof(hostname)-1] = '\0';
+
+        full_princ = talloc_asprintf(tmp_ctx, "host/%s@%s", hostname, realm);
+    }
+    if (!full_princ) {
+        DEBUG(1, ("Client principal not available.\n"));
+        goto done;
+    }
+    DEBUG(7, ("Client principal name is: [%s]\n", full_princ));
+    krberr = krb5_parse_name(context, full_princ, &client_principal);
+    if (krberr != 0) {
+        DEBUG(1, ("krb5_parse_name failed.\n"));
+        goto done;
+    }
+
+    memset(&mcred, 0, sizeof(mcred));
+    memset(&cred, 0, sizeof(mcred));
+    mcred.client = client_principal;
+    mcred.server = server_principal;
+
+    krberr = krb5_cc_retrieve_cred(context, ccache, 0, &mcred, &cred);
+    if (krberr != 0) {
+        DEBUG(1, ("krb5_cc_retrieve_cred failed.\n"));
+        goto done;
+    }
+
+    DEBUG(7, ("TGT end time [%d].\n", cred.times.endtime));
+
+    if (cred.times.endtime <= time(NULL)) {
+        DEBUG(3, ("TGT is expired.\n"));
+        result = true;
+    }
+    krb5_free_cred_contents(context, &cred);
+
+done:
+    if (client_principal != NULL) {
+        krb5_free_principal(context, client_principal);
+    }
+    if (server_principal != NULL) {
+        krb5_free_principal(context, server_principal);
+    }
+    if (ccache != NULL) {
+        if (result) {
+            krb5_cc_destroy(context, ccache);
+        } else {
+            krb5_cc_close(context, ccache);
+        }
+    }
+    if (context != NULL) krb5_free_context(context);
+    talloc_free(tmp_ctx);
+    return result;
+}
 
 int sdap_id_setup_tasks(struct sdap_id_ctx *ctx)
 {
diff --git a/server/providers/ldap/ldap_common.h b/server/providers/ldap/ldap_common.h
index b198545..ff1ffb7 100644
--- a/server/providers/ldap/ldap_common.h
+++ b/server/providers/ldap/ldap_common.h
@@ -91,6 +91,7 @@ int ldap_id_cleanup_set_timer(struct sdap_id_ctx *ctx, struct timeval tv);
 
 bool sdap_connected(struct sdap_id_ctx *ctx);
 void sdap_mark_offline(struct sdap_id_ctx *ctx);
+bool sdap_check_gssapi_reconnect(struct sdap_id_ctx *ctx);
 
 struct tevent_req *users_get_send(TALLOC_CTX *memctx,
                                   struct tevent_context *ev,
diff --git a/server/providers/ldap/ldap_id.c b/server/providers/ldap/ldap_id.c
index 18b387e..4bbc07a 100644
--- a/server/providers/ldap/ldap_id.c
+++ b/server/providers/ldap/ldap_id.c
@@ -719,6 +719,11 @@ static void sdap_account_info_users_done(struct tevent_req *req)
             dp_err = DP_ERR_OFFLINE;
             ctx = talloc_get_type(breq->be_ctx->bet_info[BET_ID].pvt_bet_data,
                                   struct sdap_id_ctx);
+            if (sdap_check_gssapi_reconnect(ctx)) {
+                talloc_zfree(ctx->gsh);
+                sdap_account_info_handler(breq);
+                return;
+            }
             sdap_mark_offline(ctx);
         }
     }
@@ -745,6 +750,11 @@ static void sdap_account_info_groups_done(struct tevent_req *req)
             dp_err = DP_ERR_OFFLINE;
             ctx = talloc_get_type(breq->be_ctx->bet_info[BET_ID].pvt_bet_data,
                                   struct sdap_id_ctx);
+            if (sdap_check_gssapi_reconnect(ctx)) {
+                talloc_zfree(ctx->gsh);
+                sdap_account_info_handler(breq);
+                return;
+            }
             sdap_mark_offline(ctx);
         }
     }
@@ -771,6 +781,11 @@ static void sdap_account_info_initgr_done(struct tevent_req *req)
             dp_err = DP_ERR_OFFLINE;
             ctx = talloc_get_type(breq->be_ctx->bet_info[BET_ID].pvt_bet_data,
                                   struct sdap_id_ctx);
+            if (sdap_check_gssapi_reconnect(ctx)) {
+                talloc_zfree(ctx->gsh);
+                sdap_account_info_handler(breq);
+                return;
+            }
             sdap_mark_offline(ctx);
         }
     }
diff --git a/server/providers/ldap/ldap_id_cleanup.c b/server/providers/ldap/ldap_id_cleanup.c
index 60d3b28..f3fb444 100644
--- a/server/providers/ldap/ldap_id_cleanup.c
+++ b/server/providers/ldap/ldap_id_cleanup.c
@@ -212,6 +212,14 @@ fail:
         DEBUG(9, ("User cleanup failed with: (%d)[%s]\n",
                   (int)err, strerror(err)));
 
+        if (sdap_check_gssapi_reconnect(state->ctx)) {
+            talloc_zfree(state->ctx->gsh);
+            subreq = cleanup_users_send(state, state->ev, state->ctx);
+            if (subreq != NULL) {
+                tevent_req_set_callback(subreq, ldap_id_cleanup_users_done, req);
+                return;
+            }
+        }
         sdap_mark_offline(state->ctx);
     }
 
@@ -242,7 +250,15 @@ static void ldap_id_cleanup_groups_done(struct tevent_req *subreq)
     return;
 
 fail:
-    /* always go offline on failures */
+    /* check if credentials are expired otherwise go offline on failures */
+    if (sdap_check_gssapi_reconnect(state->ctx)) {
+        talloc_zfree(state->ctx->gsh);
+        subreq = cleanup_groups_send(state, state->ev, state->ctx);
+        if (subreq != NULL) {
+            tevent_req_set_callback(subreq, ldap_id_cleanup_groups_done, req);
+            return;
+        }
+    }
     sdap_mark_offline(state->ctx);
     DEBUG(1, ("Failed to cleanup groups (%d [%s]), retrying later!\n",
               (int)err, strerror(err)));
diff --git a/server/providers/ldap/ldap_id_enum.c b/server/providers/ldap/ldap_id_enum.c
index 1ddcbf8..bc06e8b 100644
--- a/server/providers/ldap/ldap_id_enum.c
+++ b/server/providers/ldap/ldap_id_enum.c
@@ -227,6 +227,14 @@ fail:
         DEBUG(9, ("User enumeration failed with: (%d)[%s]\n",
                   (int)err, strerror(err)));
 
+        if (sdap_check_gssapi_reconnect(state->ctx)) {
+            talloc_zfree(state->ctx->gsh);
+            subreq = enum_users_send(state, state->ev, state->ctx, state->purge);
+            if (subreq != NULL) {
+                tevent_req_set_callback(subreq, ldap_id_enum_users_done, req);
+                return;
+            }
+        }
         sdap_mark_offline(state->ctx);
     }
 
@@ -268,7 +276,15 @@ static void ldap_id_enum_groups_done(struct tevent_req *subreq)
     return;
 
 fail:
-    /* always go offline on failures */
+    /* check if credentials are expired otherwise go offline on failures */
+    if (sdap_check_gssapi_reconnect(state->ctx)) {
+        talloc_zfree(state->ctx->gsh);
+        subreq = enum_groups_send(state, state->ev, state->ctx, state->purge);
+        if (subreq != NULL) {
+            tevent_req_set_callback(subreq, ldap_id_enum_groups_done, req);
+            return;
+        }
+    }
     sdap_mark_offline(state->ctx);
     DEBUG(1, ("Failed to enumerate groups (%d [%s]), retrying later!\n",
               (int)err, strerror(err)));
-- 
1.6.5.2



More information about the sssd-devel mailing list