summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2009-12-04 11:04:34 +0100
committerStephen Gallagher <sgallagh@redhat.com>2009-12-07 10:19:04 -0500
commit051cd4b5d3fce4174b92b9b55c1527b8166ea975 (patch)
tree6306f21cec9d552479f16e731fd4b588b878727a
parentafb62c29182fc8174b63520a7170faa22a9dc47b (diff)
downloadsssd-051cd4b5d3fce4174b92b9b55c1527b8166ea975.tar.gz
sssd-051cd4b5d3fce4174b92b9b55c1527b8166ea975.tar.xz
sssd-051cd4b5d3fce4174b92b9b55c1527b8166ea975.zip
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.
-rw-r--r--server/providers/ldap/ldap_common.c139
-rw-r--r--server/providers/ldap/ldap_common.h1
-rw-r--r--server/providers/ldap/ldap_id.c15
-rw-r--r--server/providers/ldap/ldap_id_cleanup.c18
-rw-r--r--server/providers/ldap/ldap_id_enum.c18
5 files changed, 189 insertions, 2 deletions
diff --git a/server/providers/ldap/ldap_common.c b/server/providers/ldap/ldap_common.c
index d43d14855..e4f3f6bbf 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 b1985455c..ff1ffb725 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 18b387e57..4bbc07a68 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 60d3b28ac..f3fb4443c 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 1ddcbf8fd..bc06e8bdc 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)));