diff --git a/src/providers/data_provider_fo.c b/src/providers/data_provider_fo.c index 482f744..24a045d 100644 --- a/src/providers/data_provider_fo.c +++ b/src/providers/data_provider_fo.c @@ -226,6 +226,28 @@ int be_fo_service_add_callback(TALLOC_CTX *memctx, return EOK; } +int be_fo_get_server_count(struct be_ctx *ctx, const char *service_name) +{ + struct be_svc_data* svc_data; + struct fo_service *svc; + + if (!ctx->be_fo) { + return 0; + } + + DLIST_FOR_EACH(svc_data, ctx->be_fo->svcs) { + if (strcmp(svc_data->name, service_name) == 0) { + break; + } + } + + if (!svc_data) { + return 0; + } + + return fo_get_server_count(svc_data->fo_service); +} + int be_fo_add_server(struct be_ctx *ctx, const char *service_name, const char *server, int port, void *user_data) { diff --git a/src/providers/dp_backend.h b/src/providers/dp_backend.h index f1069d0..e414c7d 100644 --- a/src/providers/dp_backend.h +++ b/src/providers/dp_backend.h @@ -130,6 +130,7 @@ int be_fo_add_service(struct be_ctx *ctx, const char *service_name); int be_fo_service_add_callback(TALLOC_CTX *memctx, struct be_ctx *ctx, const char *service_name, be_svc_callback_fn_t *fn, void *private_data); +int be_fo_get_server_count(struct be_ctx *ctx, const char *service_name); int be_fo_add_server(struct be_ctx *ctx, const char *service_name, const char *server, int port, void *user_data); diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c index 54ad032..a6aa70c 100644 --- a/src/providers/fail_over.c +++ b/src/providers/fail_over.c @@ -341,6 +341,19 @@ create_server_common(TALLOC_CTX *mem_ctx, struct fo_ctx *ctx, const char *name) } int +fo_get_server_count(struct fo_service *service) +{ + struct fo_server *server; + int count = 0; + + DLIST_FOR_EACH(server, service->server_list) { + count++; + } + + return count; +} + +int fo_add_server(struct fo_service *service, const char *name, int port, void *user_data) { diff --git a/src/providers/fail_over.h b/src/providers/fail_over.h index ffcd068..5bf396d 100644 --- a/src/providers/fail_over.h +++ b/src/providers/fail_over.h @@ -93,6 +93,11 @@ int fo_get_service(struct fo_ctx *ctx, struct fo_service **_service); /* + * Get number of servers registered for the 'service'. + */ +int fo_get_server_count(struct fo_service *service); + +/* * Adds a server 'name' to the 'service'. Port 'port' will be used for * connection. If 'name' is NULL, no server resolution will be done. */ diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c index 55a7133..179ffd2 100644 --- a/src/providers/ipa/ipa_access.c +++ b/src/providers/ipa/ipa_access.c @@ -315,6 +315,7 @@ static int hbac_get_user_info_recv(struct tevent_req *req, TALLOC_CTX *memctx, struct hbac_get_host_info_state { struct tevent_context *ev; struct sdap_id_ctx *sdap_ctx; + struct sdap_id_op *sdap_op; struct sysdb_ctx *sysdb; struct sysdb_handle *handle; bool offline; @@ -329,7 +330,7 @@ struct hbac_get_host_info_state { struct hbac_host_info **hbac_host_info; }; -static void hbac_get_host_info_connect_done(struct tevent_req *subreq); +static void hbac_get_host_info_connect_done(struct sdap_id_op* op, int ret, void* pvt); static void hbac_get_host_memberof_done(struct tevent_req *subreq); static void hbac_get_host_info_sysdb_transaction_started(struct tevent_req *subreq); static void hbac_get_host_info_store_prepare(struct tevent_req *req); @@ -430,41 +431,18 @@ static struct tevent_req *hbac_get_host_info_send(TALLOC_CTX *memctx, return req; } - - if (sdap_ctx->gsh == NULL || ! sdap_ctx->gsh->connected) { - if (sdap_ctx->gsh != NULL) { - talloc_zfree(sdap_ctx->gsh); - } - - subreq = sdap_cli_connect_send(state, ev, sdap_ctx->opts, - sdap_ctx->be, sdap_ctx->service, NULL); - if (!subreq) { - DEBUG(1, ("sdap_cli_connect_send failed.\n")); - ret = ENOMEM; - goto fail; - } - - tevent_req_set_callback(subreq, hbac_get_host_info_connect_done, req); - - return req; - } - - subreq = sdap_get_generic_send(state, state->ev, - state->sdap_ctx->opts, - state->sdap_ctx->gsh, - state->host_search_base, - LDAP_SCOPE_SUB, - state->host_filter, - state->host_attrs, - NULL, 0); - - if (subreq == NULL) { - DEBUG(1, ("sdap_get_generic_send failed.\n")); + + state->sdap_op = sdap_id_op_create(state, sdap_ctx); + if (!state->sdap_op) { + DEBUG(1, ("sdap_id_op_create failed.\n")); ret = ENOMEM; goto fail; } - - tevent_req_set_callback(subreq, hbac_get_host_memberof_done, req); + + sdap_id_op_connect(state->sdap_op, hbac_get_host_info_connect_done, req); + if (!tevent_req_is_in_progress(req)) { + tevent_req_post(req, ev); + } return req; @@ -474,17 +452,14 @@ fail: return req; } -static void hbac_get_host_info_connect_done(struct tevent_req *subreq) +static void hbac_get_host_info_connect_done(struct sdap_id_op* op, int ret, void* pvt) { - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); + struct tevent_req *req = talloc_get_type(pvt, + struct tevent_req); struct hbac_get_host_info_state *state = tevent_req_data(req, struct hbac_get_host_info_state); - int ret; + struct tevent_req *subreq; - ret = sdap_cli_connect_recv(subreq, state->sdap_ctx, &state->sdap_ctx->gsh, - NULL); - talloc_zfree(subreq); if (ret) { tevent_req_error(req, ret); return; @@ -492,7 +467,7 @@ static void hbac_get_host_info_connect_done(struct tevent_req *subreq) subreq = sdap_get_generic_send(state, state->ev, state->sdap_ctx->opts, - state->sdap_ctx->gsh, + sdap_id_op_handle(op), state->host_search_base, LDAP_SCOPE_SUB, state->host_filter, @@ -804,6 +779,7 @@ static int hbac_get_host_info_recv(struct tevent_req *req, TALLOC_CTX *memctx, struct hbac_get_rules_state { struct tevent_context *ev; struct sdap_id_ctx *sdap_ctx; + struct sdap_id_op *sdap_op; struct sysdb_ctx *sysdb; struct sysdb_handle *handle; bool offline; @@ -820,7 +796,7 @@ struct hbac_get_rules_state { int current_item; }; -static void hbac_get_rules_connect_done(struct tevent_req *subreq); +static void hbac_get_rules_connect_done(struct sdap_id_op* op, int ret, void* pvt); static void hbac_rule_get_done(struct tevent_req *subreq); static void hbac_rule_sysdb_transaction_started(struct tevent_req *subreq); static void hbac_rule_sysdb_delete_done(struct tevent_req *subreq); @@ -939,41 +915,18 @@ static struct tevent_req *hbac_get_rules_send(TALLOC_CTX *memctx, return req; } - - if (sdap_ctx->gsh == NULL || ! sdap_ctx->gsh->connected) { - if (sdap_ctx->gsh != NULL) { - talloc_zfree(sdap_ctx->gsh); - } - - subreq = sdap_cli_connect_send(state, ev, sdap_ctx->opts, - sdap_ctx->be, sdap_ctx->service, NULL); - if (!subreq) { - DEBUG(1, ("sdap_cli_connect_send failed.\n")); - ret = ENOMEM; - goto fail; - } - - tevent_req_set_callback(subreq, hbac_get_rules_connect_done, req); - - return req; - } - - subreq = sdap_get_generic_send(state, state->ev, - state->sdap_ctx->opts, - state->sdap_ctx->gsh, - state->hbac_search_base, - LDAP_SCOPE_SUB, - state->hbac_filter, - state->hbac_attrs, - NULL, 0); - - if (subreq == NULL) { - DEBUG(1, ("sdap_get_generic_send failed.\n")); + + state->sdap_op = sdap_id_op_create(state, sdap_ctx); + if (!state->sdap_op) { + DEBUG(1, ("sdap_id_op_create failed.\n")); ret = ENOMEM; goto fail; } - - tevent_req_set_callback(subreq, hbac_rule_get_done, req); + + sdap_id_op_connect(state->sdap_op, hbac_get_rules_connect_done, req); + if (!tevent_req_is_in_progress(req)) { + tevent_req_post(req, ev); + } return req; @@ -983,17 +936,14 @@ fail: return req; } -static void hbac_get_rules_connect_done(struct tevent_req *subreq) +static void hbac_get_rules_connect_done(struct sdap_id_op* op, int ret, void* pvt) { - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); + struct tevent_req *req = talloc_get_type(pvt, + struct tevent_req); struct hbac_get_rules_state *state = tevent_req_data(req, struct hbac_get_rules_state); - int ret; + struct tevent_req *subreq; - ret = sdap_cli_connect_recv(subreq, state->sdap_ctx, &state->sdap_ctx->gsh, - NULL); - talloc_zfree(subreq); if (ret) { tevent_req_error(req, ret); return; @@ -1001,7 +951,7 @@ static void hbac_get_rules_connect_done(struct tevent_req *subreq) subreq = sdap_get_generic_send(state, state->ev, state->sdap_ctx->opts, - state->sdap_ctx->gsh, + sdap_id_op_handle(op), state->hbac_search_base, LDAP_SCOPE_SUB, state->hbac_filter, diff --git a/src/providers/ldap/ldap_child.c b/src/providers/ldap/ldap_child.c index 6a78ca0..8ae1735 100644 --- a/src/providers/ldap/ldap_child.c +++ b/src/providers/ldap/ldap_child.c @@ -89,13 +89,13 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size, return EOK; } -static int pack_buffer(struct response *r, int result, const char *msg) +static int pack_buffer(struct response *r, int result, const char *msg, time_t expire_time) { int len; size_t p = 0; len = strlen(msg); - r->size = 2 * sizeof(uint32_t) + len; + r->size = 2 * sizeof(uint32_t) + len + sizeof(time_t); r->buf = talloc_array(r, uint8_t, r->size); if(!r->buf) { @@ -110,6 +110,9 @@ static int pack_buffer(struct response *r, int result, const char *msg) /* message itself */ safealign_memcpy(&r->buf[p], msg, len, &p); + + /* ticket expiration time */ + safealign_memcpy(&r->buf[p], &expire_time, sizeof(expire_time), &p); return EOK; } @@ -118,7 +121,8 @@ static int ldap_child_get_tgt_sync(TALLOC_CTX *memctx, const char *realm_str, const char *princ_str, const char *keytab_name, - const char **ccname_out) + const char **ccname_out, + time_t *expire_time_out) { char *ccname; char *realm_name = NULL; @@ -220,8 +224,6 @@ static int ldap_child_get_tgt_sync(TALLOC_CTX *memctx, krb5_get_init_creds_opt_set_address_list(&options, NULL); krb5_get_init_creds_opt_set_forwardable(&options, 0); krb5_get_init_creds_opt_set_proxiable(&options, 0); - /* set a very short lifetime, we don't keep the ticket around */ - krb5_get_init_creds_opt_set_tkt_life(&options, 300); krberr = krb5_get_init_creds_keytab(context, &my_creds, kprinc, keytab, 0, NULL, &options); @@ -240,7 +242,7 @@ static int ldap_child_get_tgt_sync(TALLOC_CTX *memctx, ret = EFAULT; goto done; } - + krberr = krb5_cc_store_cred(context, ccache, &my_creds); if (krberr) { DEBUG(2, ("Failed to store creds: %s\n", @@ -248,9 +250,11 @@ static int ldap_child_get_tgt_sync(TALLOC_CTX *memctx, ret = EFAULT; goto done; } - + + ret = EOK; *ccname_out = ccname; + *expire_time_out = my_creds.times.endtime; done: if (keytab) krb5_kt_close(context, keytab); @@ -260,6 +264,7 @@ done: static int prepare_response(TALLOC_CTX *mem_ctx, const char *ccname, + time_t expire_time, krb5_error_code kerr, struct response **rsp) { @@ -274,7 +279,7 @@ static int prepare_response(TALLOC_CTX *mem_ctx, r->size = 0; if (kerr == 0) { - ret = pack_buffer(r, EOK, ccname); + ret = pack_buffer(r, EOK, ccname, expire_time); } else { krb5_msg = sss_krb5_get_error_message(krb5_error_ctx, kerr); if (krb5_msg == NULL) { @@ -282,7 +287,7 @@ static int prepare_response(TALLOC_CTX *mem_ctx, return ENOMEM; } - ret = pack_buffer(r, EFAULT, krb5_msg); + ret = pack_buffer(r, EFAULT, krb5_msg, 0); sss_krb5_free_error_message(krb5_error_ctx, krb5_msg); } @@ -306,6 +311,7 @@ int main(int argc, const char *argv[]) uint8_t *buf = NULL; ssize_t len = 0; const char *ccname = NULL; + time_t expire_time = 0; struct input_buffer *ibuf = NULL; struct response *resp = NULL; size_t written; @@ -392,13 +398,13 @@ int main(int argc, const char *argv[]) kerr = ldap_child_get_tgt_sync(main_ctx, ibuf->realm_str, ibuf->princ_str, - ibuf->keytab_name, &ccname); + ibuf->keytab_name, &ccname, &expire_time); if (kerr != EOK) { DEBUG(1, ("ldap_child_get_tgt_sync failed.\n")); /* Do not return, must report failure */ } - ret = prepare_response(main_ctx, ccname, kerr, &resp); + ret = prepare_response(main_ctx, ccname, expire_time, kerr, &resp); if (ret != EOK) { DEBUG(1, ("prepare_response failed. [%d][%s].\n", ret, strerror(ret))); return ENOMEM; diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c index b5765c2..6f93998 100644 --- a/src/providers/ldap/ldap_common.c +++ b/src/providers/ldap/ldap_common.c @@ -24,8 +24,9 @@ #include "providers/ldap/ldap_common.h" #include "providers/fail_over.h" +#include "providers/ldap/sdap_async.h" -#include "util/sss_krb5.h" +#include /* a fd the child process would log into */ int ldap_child_debug_fd = -1; @@ -345,162 +346,486 @@ void sdap_handler_done(struct be_req *req, int dp_err, return req->fn(req, dp_err, error, errstr); } -bool sdap_connected(struct sdap_id_ctx *ctx) +static int sdap_id_connection_destroy(struct sdap_id_connection*); +static bool sdap_is_connection_expired(struct sdap_id_connection *connection, int timeout); +static bool sdap_can_reuse_connection(struct sdap_id_connection *connection); + +static int sdap_id_connection_set_expire_timer(struct sdap_id_connection *connection); +static void sdap_id_connection_expire_handler(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *pvt); + +static int sdap_id_op_destroy(struct sdap_id_op*); +static void sdap_id_op_hook_connection(struct sdap_id_op *op, struct sdap_id_connection *connection); +static void sdap_id_op_connect_done(struct tevent_req *subreq); + +static int sdap_id_connection_destroy(struct sdap_id_connection* connection) { - if (ctx->gsh) { - return ctx->gsh->connected; + struct sdap_id_op* op; + + /* we clean out list of ops to make sure that order of destruction does not matter */ + while ((op = connection->ops) != NULL) { + op->connection = NULL; + + /* we are not calling connect callback as the whole context is going down, + * so there is no need to bother */ + op->connect_callback = NULL; + op->connect_data = NULL; + + DLIST_REMOVE(connection->ops, op); } - return false; + return 0; } -void sdap_mark_offline(struct sdap_id_ctx *ctx) +struct sdap_id_op* sdap_id_op_create(TALLOC_CTX *memctx, struct sdap_id_ctx *ctx) { - if (ctx->gsh) { - /* make sure we mark the connection as gone when we go offline so that - * we do not try to reuse a bad connection by mistale later */ - talloc_zfree(ctx->gsh); - } + struct sdap_id_op* op = talloc_zero(memctx, struct sdap_id_op); + if (!op) { + return NULL; + } + + op->ctx = ctx; + + talloc_set_destructor(op, sdap_id_op_destroy); + return op; +} - be_mark_offline(ctx->be); +static int sdap_id_op_destroy(struct sdap_id_op* op) +{ + sdap_id_op_hook_connection(op, NULL); + return 0; } -bool sdap_check_gssapi_reconnect(struct sdap_id_ctx *ctx) +static void sdap_id_release_connection(struct sdap_id_connection *connection) { - 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; + struct sdap_id_ctx *ctx; + if (!connection || connection->ops || connection->notify_lock) { + /* connection is in use */ + return; } - - realm = dp_opt_get_string(ctx->opts->basic, SDAP_KRB5_REALM); - if (realm == NULL) { - DEBUG(3, ("Kerberos realm not available.\n")); - return false; + + ctx = connection->ctx; + if (connection == ctx->cached_connection) { + return; } + + DEBUG(9, ("releasing unused connection\n")); - tmp_ctx = talloc_new(NULL); - if (tmp_ctx == NULL) { - DEBUG(1, ("talloc_new failed.\n")); - return false; - } + DLIST_REMOVE(ctx->connections, connection); + talloc_zfree(connection); +} - ccname = talloc_asprintf(tmp_ctx, "FILE:%s/ccache_%s", DB_PATH, realm); - if (ccname == NULL) { - DEBUG(1, ("talloc_asprintf failed.\n")); - goto done; +static void sdap_id_op_hook_connection(struct sdap_id_op *op, struct sdap_id_connection *connection) +{ + struct sdap_id_connection *current = op->connection; + if (connection == current) { + return; } - - krberr = krb5_init_context(&context); - if (krberr) { - DEBUG(1, ("Failed to init kerberos context\n")); - goto done; + + if (current) { + DLIST_REMOVE(current->ops, op); } - krberr = krb5_cc_resolve(context, ccname, &ccache); - if (krberr != 0) { - DEBUG(1, ("krb5_cc_resolve failed.\n")); - goto done; + op->connection = connection; + + if (connection) { + DLIST_ADD_END(connection->ops, op, struct sdap_id_op*); + } + + if (current) { + sdap_id_release_connection(current); } +} - server_name = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", realm, realm); - if (server_name == NULL) { - DEBUG(1, ("talloc_asprintf failed.\n")); - goto done; +static bool sdap_id_op_can_reconnect(struct sdap_id_op *op) +{ + /* we allow 2 retries for failover server configured: + * - one for connection broken during request execution + * - one for the following (probably failed) reconnect attempt */ + int max_retries = 2 * be_fo_get_server_count(op->ctx->be, op->ctx->service->name) - 1; + if (max_retries < 1) { + max_retries = 1; + } + + return op->reconnect_retry_count < max_retries; +} + +void sdap_id_op_connect(struct sdap_id_op *op, sdap_id_connect_callback_t callback, void *data) +{ + int error = EOK; + struct sdap_id_ctx *ctx = op->ctx; + struct sdap_id_connection *connection; + struct tevent_req *subreq; + + /* Try to reuse own connection */ + connection = op->connection; + if (connection) { + if (sdap_can_reuse_connection(connection)) { + DEBUG(9, ("reusing operation connection\n")); + callback(op, EOK, data); + return; + } + + if (ctx->cached_connection == connection) { + ctx->cached_connection = NULL; + } + + DEBUG(9, ("releasing operation connection\n")); + sdap_id_op_hook_connection(op, NULL); + } + + /* Try to reuse context cached connection */ + connection = ctx->cached_connection; + if (connection) { + if (connection->connect_req) { + DEBUG(9, ("waiting for connection to complete\n")); + sdap_id_op_hook_connection(op, connection); + op->connect_callback = callback; + op->connect_data = data; + return; + } + + if (sdap_can_reuse_connection(connection)) { + DEBUG(9, ("reusing cached connection\n")); + sdap_id_op_hook_connection(op, connection); + callback(op, EOK, data); + return; + } + + DEBUG(9, ("releasing expired cached connection\n")); + ctx->cached_connection = NULL; + sdap_id_release_connection(connection); + } + + DEBUG(9, ("beginning to connect\n")); + + connection = talloc_zero(ctx, struct sdap_id_connection); + if (!connection) { + error = ENOMEM; + goto fail; + } + + talloc_set_destructor(connection, sdap_id_connection_destroy); + + connection->ctx = ctx; + subreq = sdap_cli_connect_send(connection,ctx->be->ev, + ctx->opts, ctx->be, + ctx->service, &connection->rootDSE); + if (!subreq) { + error = ENOMEM; + goto fail; + } + + tevent_req_set_callback(subreq, sdap_id_op_connect_done, connection); + connection->connect_req = subreq; + + DLIST_ADD(ctx->connections, connection); + ctx->cached_connection = connection; + + sdap_id_op_hook_connection(op, connection); + op->connect_callback = callback; + op->connect_data = data; + return; + +fail: + if (connection) { + sdap_id_release_connection(connection); + } + + callback(op, error, data); + return; +} + +static void sdap_id_op_call_connect_callback(struct sdap_id_op* op, int retval) +{ + sdap_id_connect_callback_t callback = op->connect_callback; + void* data = op->connect_data; + + if (!callback) { + return; } + + op->connect_callback = NULL; + op->connect_data = NULL; + + callback(op, retval, data); +} - krberr = krb5_parse_name(context, server_name, &server_principal); - if (krberr != 0) { - DEBUG(1, ("krb5_parse_name failed.\n")); - goto done; +static void sdap_id_op_connect_done(struct tevent_req *subreq) +{ + struct sdap_id_connection *connection = tevent_req_callback_data(subreq, + struct sdap_id_connection); + struct sdap_id_ctx *ctx = connection->ctx; + bool can_retry = false; + int ret; + + ret = sdap_cli_connect_recv(subreq, connection, &can_retry, &connection->sh, &connection->rootDSE); + connection->connect_req = NULL; + talloc_zfree(subreq); + + connection->notify_lock++; + + if (ret == ENOTSUP) { + DEBUG(0, ("Authentication mechanism not Supported by server\n")); + } + + if (ret == EOK && (!connection->sh || !connection->sh->connected)) { + DEBUG(0, ("sdap_cli_connect_recv returned bogus connection\n")); + ret = EFAULT; + } + + if (ret != EOK && !can_retry) { + /* be is going offline as there is no more servers to try */ + DEBUG(1, ("Failed to connect, going offline (%d [%s])\n", + ret, strerror(ret))); + be_mark_offline(connection->ctx->be); + } + + if (ret == EOK) { + ret = sdap_id_connection_set_expire_timer(connection); + } + + if (can_retry) { + switch (ret) { + case EOK: + case ENOTSUP: + case EACCES: + case EIO: + case EFAULT: + case ETIMEDOUT: + break; + + default: + /* do not attempt to retry on errors like ENOMEM */ + can_retry = false; + break; + } } + + int notify_count = 0; + + /* Notify about connection */ + for(;;) { + struct sdap_id_op* op; + + if (ret == EOK && !connection->sh->connected) { + DEBUG(9, ("connection was broken after %d notifies\n", notify_count)); + } - 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); + DLIST_FOR_EACH(op, connection->ops) { + if (op->connect_callback) { + break; + } + } + + if (!op) { + break; + } + + /* another connection to notify */ + notify_count++; + + if (ret != EOK || !connection->sh->connected) { + /* failed to connect or connection got broken during notify */ + bool retry = false; + + /* drop connection from cache now */ + if (ctx->cached_connection == connection) { + ctx->cached_connection = NULL; + } + + if (can_retry) { + /* determining whether retry is possible */ + if (be_is_offline(ctx->be)) { + /* be is offline, no retry possible */ + if (ret == EOK) { + DEBUG(9, ("skipping automatic retry on op #%d as be is offline\n", notify_count)); + ret = EIO; + } + + can_retry = false; + } else { + if (ret == EOK) { + DEBUG(9, ("attempting automatic retry on op #%d\n", notify_count)); + retry = true; + } else if (sdap_id_op_can_reconnect(op)) { + DEBUG(9, ("attempting failover retry on op #%d\n", notify_count)); + op->reconnect_retry_count++; + retry = true; + } + } + } + + if (retry) { + sdap_id_connect_callback_t callback = op->connect_callback; + void* data = op->connect_data; + + op->connect_callback = NULL; + op->connect_data = NULL; + + sdap_id_op_hook_connection(op, NULL); + sdap_id_op_connect(op, callback, data); + continue; + } + } + + if (ret == EOK) { + DEBUG(9, ("notify connected to op #%d\n", notify_count)); } else { - full_princ = talloc_strdup(tmp_ctx, client_princ_str); + DEBUG(9, ("notify error to op #%d: %d [%s]\n", notify_count, ret, strerror(ret))); } + + sdap_id_op_call_connect_callback(op, ret); + } + + /* all connections notified */ + if (connection->notify_lock > 0) { + connection->notify_lock--; + } + + if (ret == EOK && connection->sh->connected && !be_is_offline(ctx->be)) { + DEBUG(9, ("caching successful connection after %d notifies\n", notify_count)); + ctx->cached_connection = connection; } 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; + if (ctx->cached_connection == connection) { + ctx->cached_connection = NULL; } - hostname[sizeof(hostname)-1] = '\0'; + + sdap_id_release_connection(connection); + } +} - full_princ = talloc_asprintf(tmp_ctx, "host/%s@%s", hostname, realm); +static int sdap_id_connection_set_expire_timer(struct sdap_id_connection *connection) +{ + int timeout; + struct timeval tv; + + memset(&tv, 0, sizeof(tv)); + + tv.tv_sec = connection->sh->expire_time; + if (tv.tv_sec <= 0) { + return EOK; } - if (!full_princ) { - DEBUG(1, ("Client principal not available.\n")); - goto done; + + timeout = dp_opt_get_int(connection->ctx->opts->basic, SDAP_OPT_TIMEOUT); + if (timeout > 0) { + tv.tv_sec -= timeout; } - 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; + + if (tv.tv_sec <= time(NULL)) { + return EOK; } + + talloc_zfree(connection->expire_timer); - 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; + connection->expire_timer = tevent_add_timer(connection->ctx->be->ev, + connection, + tv, + sdap_id_connection_expire_handler, + connection); + if (!connection->expire_timer) { + return ENOMEM; } + + return EOK; +} - DEBUG(7, ("TGT end time [%d].\n", cred.times.endtime)); +static void sdap_id_connection_expire_handler(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *pvt) +{ + struct sdap_id_connection *connection = talloc_get_type(pvt, + struct sdap_id_connection); + struct sdap_id_ctx* ctx = connection->ctx; - if (cred.times.endtime <= time(NULL)) { - DEBUG(3, ("TGT is expired.\n")); - result = true; + DEBUG(3, ("connection is about to expire, releasing it\n")); + + if (ctx->cached_connection == connection) { + ctx->cached_connection = NULL; + + sdap_id_release_connection(connection); } - krb5_free_cred_contents(context, &cred); +} -done: - if (client_principal != NULL) { - krb5_free_principal(context, client_principal); +static bool sdap_is_connection_expired(struct sdap_id_connection *connection, int timeout) +{ + time_t expire_time; + if (!connection || !connection->sh || !connection->sh->connected) { + return true; } - if (server_principal != NULL) { - krb5_free_principal(context, server_principal); + + expire_time = connection->sh->expire_time; + if (expire_time != 0 && expire_time < time( NULL ) + timeout ) { + return true; } - if (ccache != NULL) { - if (result) { - krb5_cc_destroy(context, ccache); + + return false; +} + +static bool sdap_can_reuse_connection(struct sdap_id_connection *connection) +{ + int timeout; + + if (!connection || !connection->sh || !connection->sh->connected) { + return false; + } + + timeout = dp_opt_get_int(connection->ctx->opts->basic, SDAP_OPT_TIMEOUT); + return !sdap_is_connection_expired(connection, timeout); +} + +int sdap_id_op_done(struct sdap_id_op *op, int retval, int *dp_err_out) +{ + bool communication_error; + switch (retval) { + case EIO: + /* this currently the only possible communication error after connection is established */ + communication_error = true; + break; + + default: + communication_error = false; + break; + } + + if (communication_error && op->connection != 0 && op->connection == op->ctx->cached_connection) { + /* do not reuse failed connection */ + op->ctx->cached_connection = NULL; + } + + int dp_err; + if (retval == EOK) { + dp_err = DP_ERR_OK; + } else if (be_is_offline(op->ctx->be)) { + /* if backend is already offline, just report offline, do not duplicate errors */ + dp_err = DP_ERR_OFFLINE; + retval = EAGAIN; + } else if (communication_error) { + /* communication error, can try to reconnect */ + + if (!sdap_id_op_can_reconnect(op)) { + dp_err = DP_ERR_FATAL; } else { - krb5_cc_close(context, ccache); + dp_err = DP_ERR_OK; + retval = EAGAIN; } + } else { + dp_err = DP_ERR_FATAL; } - if (context != NULL) krb5_free_context(context); - talloc_free(tmp_ctx); - return result; + + if (dp_err == DP_ERR_OK && retval != EOK) { + /* reconnect retry */ + op->reconnect_retry_count++; + } else { + /* end of request */ + op->reconnect_retry_count = 0; + } + + *dp_err_out = dp_err; + return retval; } int sdap_id_setup_tasks(struct sdap_id_ctx *ctx) diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h index ff1ffb7..cb62069 100644 --- a/src/providers/ldap/ldap_common.h +++ b/src/providers/ldap/ldap_common.h @@ -33,17 +33,17 @@ /* a fd the child process would log into */ extern int ldap_child_debug_fd; +struct sdap_id_connection; + struct sdap_id_ctx { struct be_ctx *be; struct sdap_options *opts; - struct fo_service *fo_service; struct sdap_service *service; - /* what rootDSE returns */ - struct sysdb_attrs *rootDSE; - - /* global sdap handler */ - struct sdap_handle *gsh; + /* list of all connections */ + struct sdap_id_connection *connections; + /* cached connection */ + struct sdap_id_connection *cached_connection; /* enumeration loop timer */ struct timeval last_enum; @@ -54,6 +54,44 @@ struct sdap_id_ctx { char *max_group_timestamp; }; +struct sdap_id_op; +typedef void (*sdap_id_connect_callback_t)(struct sdap_id_op* op, int, void*); + +/* LDAP ID backend extablished connection data */ +struct sdap_id_connection { + /* ID backend context */ + struct sdap_id_ctx* ctx; + /* double linked list pointers */ + struct sdap_id_connection *prev, *next; + /* sdap handle */ + struct sdap_handle *sh; + /* what rootDSE returns */ + struct sysdb_attrs *rootDSE; + /* connection request */ + struct tevent_req *connect_req; + /* timer for connection expiration */ + struct tevent_timer *expire_timer; + /* number of running connection notifies */ + int notify_lock; + /* list of operations using connect */ + struct sdap_id_op *ops; +}; + +/* LDAP ID backend hi-level operation handle with reconnect support */ +struct sdap_id_op { + /* ID backend context */ + struct sdap_id_ctx* ctx; + /* double linked list pointers */ + struct sdap_id_op *prev, *next; + /* current connection */ + struct sdap_id_connection *connection; + /* number of reconnects for this operation */ + int reconnect_retry_count; + /* connection callback */ + sdap_id_connect_callback_t connect_callback; + void *connect_data; +}; + struct sdap_auth_ctx { struct be_ctx *be; struct sdap_options *opts; @@ -61,6 +99,21 @@ struct sdap_auth_ctx { struct sdap_service *service; }; +/* common */ +struct sdap_id_op* sdap_id_op_create(TALLOC_CTX *memctx, struct sdap_id_ctx *ctx); +void sdap_id_op_connect(struct sdap_id_op*, sdap_id_connect_callback_t callback, void *data); +int sdap_id_op_done(struct sdap_id_op*, int ret, int* dp_error); + +static inline struct sdap_handle* sdap_id_op_handle(struct sdap_id_op* op) +{ + return op->connection ? op->connection->sh : NULL; +} + +static inline const struct sysdb_attrs* sdap_id_op_rootDSE(struct sdap_id_op* op) +{ + return op->connection ? op->connection->rootDSE : NULL; +} + /* id */ void sdap_account_info_handler(struct be_req *breq); int sdap_id_setup_tasks(struct sdap_id_ctx *ctx); @@ -89,13 +142,9 @@ int ldap_get_options(TALLOC_CTX *memctx, int ldap_id_enumerate_set_timer(struct sdap_id_ctx *ctx, struct timeval tv); 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, - struct sdap_id_ctx *ctx, + struct sdap_id_op *op, const char *name, int filter_type, int attrs_type); @@ -103,7 +152,7 @@ int users_get_recv(struct tevent_req *req); struct tevent_req *groups_get_send(TALLOC_CTX *memctx, struct tevent_context *ev, - struct sdap_id_ctx *ctx, + struct sdap_id_op *op, const char *name, int filter_type, int attrs_type); diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c index 4bbc07a..d761663 100644 --- a/src/providers/ldap/ldap_id.c +++ b/src/providers/ldap/ldap_id.c @@ -35,7 +35,7 @@ struct users_get_state { struct tevent_context *ev; - struct sdap_id_ctx *ctx; + struct sdap_id_op *op; struct sysdb_ctx *sysdb; struct sss_domain_info *domain; @@ -46,19 +46,19 @@ struct users_get_state { const char **attrs; }; -static void users_get_connect_done(struct tevent_req *subreq); static void users_get_done(struct tevent_req *subreq); static void users_get_delete(struct tevent_req *subreq); struct tevent_req *users_get_send(TALLOC_CTX *memctx, struct tevent_context *ev, - struct sdap_id_ctx *ctx, + struct sdap_id_op *op, const char *name, int filter_type, int attrs_type) { struct tevent_req *req, *subreq; struct users_get_state *state; + struct sdap_id_ctx *ctx = op->ctx; const char *attr_name; int ret; @@ -66,9 +66,9 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx, if (!req) return NULL; state->ev = ev; - state->ctx = ctx; + state->op = op; state->sysdb = ctx->be->sysdb; - state->domain = state->ctx->be->domain; + state->domain = ctx->be->domain; state->name = name; state->filter_type = filter_type; @@ -98,28 +98,9 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx, SDAP_OPTS_USER, &state->attrs); if (ret != EOK) goto fail; - if (!sdap_connected(ctx)) { - - if (ctx->gsh) talloc_zfree(ctx->gsh); - - /* FIXME: add option to decide if tls should be used - * or SASL/GSSAPI, etc ... */ - subreq = sdap_cli_connect_send(state, ev, ctx->opts, - ctx->be, ctx->service, - &ctx->rootDSE); - if (!subreq) { - ret = ENOMEM; - goto fail; - } - - tevent_req_set_callback(subreq, users_get_connect_done, req); - - return req; - } - subreq = sdap_get_users_send(state, state->ev, state->domain, state->sysdb, - state->ctx->opts, state->ctx->gsh, + state->op->ctx->opts, sdap_id_op_handle(op), state->attrs, state->filter); if (!subreq) { ret = ENOMEM; @@ -135,36 +116,6 @@ fail: return req; } -static void users_get_connect_done(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); - struct users_get_state *state = tevent_req_data(req, - struct users_get_state); - int ret; - - ret = sdap_cli_connect_recv(subreq, state->ctx, - &state->ctx->gsh, &state->ctx->rootDSE); - talloc_zfree(subreq); - if (ret) { - if (ret == ENOTSUP) { - DEBUG(0, ("Authentication mechanism not Supported by server")); - } - tevent_req_error(req, ret); - return; - } - - subreq = sdap_get_users_send(state, state->ev, - state->domain, state->sysdb, - state->ctx->opts, state->ctx->gsh, - state->attrs, state->filter); - if (!subreq) { - tevent_req_error(req, ENOMEM); - return; - } - tevent_req_set_callback(subreq, users_get_done, req); -} - static void users_get_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, @@ -258,7 +209,7 @@ int users_get_recv(struct tevent_req *req) struct groups_get_state { struct tevent_context *ev; - struct sdap_id_ctx *ctx; + struct sdap_id_op *op; struct sysdb_ctx *sysdb; struct sss_domain_info *domain; @@ -269,17 +220,17 @@ struct groups_get_state { const char **attrs; }; -static void groups_get_connect_done(struct tevent_req *subreq); static void groups_get_done(struct tevent_req *subreq); static void groups_get_delete(struct tevent_req *subreq); struct tevent_req *groups_get_send(TALLOC_CTX *memctx, struct tevent_context *ev, - struct sdap_id_ctx *ctx, + struct sdap_id_op *op, const char *name, int filter_type, int attrs_type) { + struct sdap_id_ctx *ctx = op->ctx; struct tevent_req *req, *subreq; struct groups_get_state *state; const char *attr_name; @@ -289,9 +240,9 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx, if (!req) return NULL; state->ev = ev; - state->ctx = ctx; + state->op = op; state->sysdb = ctx->be->sysdb; - state->domain = state->ctx->be->domain; + state->domain = ctx->be->domain; state->name = name; state->filter_type = filter_type; @@ -321,28 +272,9 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx, SDAP_OPTS_GROUP, &state->attrs); if (ret != EOK) goto fail; - if (!sdap_connected(ctx)) { - - if (ctx->gsh) talloc_zfree(ctx->gsh); - - /* FIXME: add option to decide if tls should be used - * or SASL/GSSAPI, etc ... */ - subreq = sdap_cli_connect_send(state, ev, ctx->opts, - ctx->be, ctx->service, - &ctx->rootDSE); - if (!subreq) { - ret = ENOMEM; - goto fail; - } - - tevent_req_set_callback(subreq, groups_get_connect_done, req); - - return req; - } - subreq = sdap_get_groups_send(state, state->ev, state->domain, state->sysdb, - state->ctx->opts, state->ctx->gsh, + state->op->ctx->opts, sdap_id_op_handle(op), state->attrs, state->filter); if (!subreq) { ret = ENOMEM; @@ -358,36 +290,6 @@ fail: return req; } -static void groups_get_connect_done(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); - struct groups_get_state *state = tevent_req_data(req, - struct groups_get_state); - int ret; - - ret = sdap_cli_connect_recv(subreq, state->ctx, - &state->ctx->gsh, &state->ctx->rootDSE); - talloc_zfree(subreq); - if (ret) { - if (ret == ENOTSUP) { - DEBUG(0, ("Authentication mechanism not Supported by server")); - } - tevent_req_error(req, ret); - return; - } - - subreq = sdap_get_groups_send(state, state->ev, - state->domain, state->sysdb, - state->ctx->opts, state->ctx->gsh, - state->attrs, state->filter); - if (!subreq) { - tevent_req_error(req, ENOMEM); - return; - } - tevent_req_set_callback(subreq, groups_get_done, req); -} - static void groups_get_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, @@ -481,19 +383,19 @@ int groups_get_recv(struct tevent_req *req) struct groups_by_user_state { struct tevent_context *ev; - struct sdap_id_ctx *ctx; + struct sdap_id_op *op; const char *name; const char **attrs; }; -static void groups_by_user_connect_done(struct tevent_req *subreq); static void groups_by_user_done(struct tevent_req *subreq); static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx, struct tevent_context *ev, - struct sdap_id_ctx *ctx, + struct sdap_id_op *op, const char *name) { + struct sdap_id_ctx *ctx = op->ctx; struct tevent_req *req, *subreq; struct groups_by_user_state *state; int ret; @@ -502,36 +404,17 @@ static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx, if (!req) return NULL; state->ev = ev; - state->ctx = ctx; + state->op = op; state->name = name; ret = build_attrs_from_map(state, ctx->opts->group_map, SDAP_OPTS_GROUP, &state->attrs); if (ret != EOK) goto fail; - if (!sdap_connected(ctx)) { - - if (ctx->gsh) talloc_zfree(ctx->gsh); - - /* FIXME: add option to decide if tls should be used - * or SASL/GSSAPI, etc ... */ - subreq = sdap_cli_connect_send(state, ev, ctx->opts, - ctx->be, ctx->service, - &ctx->rootDSE); - if (!subreq) { - ret = ENOMEM; - goto fail; - } - - tevent_req_set_callback(subreq, groups_by_user_connect_done, req); - - return req; - } - subreq = sdap_get_initgr_send(state, state->ev, - state->ctx->be->domain, - state->ctx->be->sysdb, - state->ctx->opts, state->ctx->gsh, + state->op->ctx->be->domain, + state->op->ctx->be->sysdb, + state->op->ctx->opts, sdap_id_op_handle(op), state->name, state->attrs); if (!subreq) { ret = ENOMEM; @@ -547,37 +430,6 @@ fail: return req; } -static void groups_by_user_connect_done(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); - struct groups_by_user_state *state = tevent_req_data(req, - struct groups_by_user_state); - int ret; - - ret = sdap_cli_connect_recv(subreq, state->ctx, - &state->ctx->gsh, &state->ctx->rootDSE); - talloc_zfree(subreq); - if (ret) { - if (ret == ENOTSUP) { - DEBUG(0, ("Authentication mechanism not Supported by server")); - } - tevent_req_error(req, ret); - return; - } - - subreq = sdap_get_initgr_send(state, state->ev, - state->ctx->be->domain, - state->ctx->be->sysdb, - state->ctx->opts, state->ctx->gsh, - state->name, state->attrs); - if (!subreq) { - tevent_req_error(req, ENOMEM); - return; - } - tevent_req_set_callback(subreq, groups_by_user_done, req); -} - static void groups_by_user_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, @@ -607,6 +459,16 @@ int groups_by_user_recv(struct tevent_req *req) /* FIXME: embed this function in sssd_be and only call out * specific functions from modules ? */ + +struct sdap_account_info_state { + struct be_req *breq; + struct sdap_id_op *op; +}; + +static struct sdap_account_info_state *sdap_account_info_create_state( + struct sdap_id_ctx *ctx, struct be_req *breq); +static void sdap_account_info_handler_retry(struct sdap_account_info_state* state); +static void sdap_account_request_connect_done(struct sdap_id_op* op, int ret, void* pvt); static void sdap_account_info_users_done(struct tevent_req *req); static void sdap_account_info_groups_done(struct tevent_req *req); @@ -616,9 +478,7 @@ void sdap_account_info_handler(struct be_req *breq) { struct sdap_id_ctx *ctx; struct be_acct_req *ar; - struct tevent_req *req; - const char *err = "Unknown Error"; - int ret = EOK; + struct sdap_account_info_state* state = NULL; ctx = talloc_get_type(breq->be_ctx->bet_info[BET_ID].pvt_bet_data, struct sdap_id_ctx); @@ -636,59 +496,136 @@ void sdap_account_info_handler(struct be_req *breq) return sdap_handler_done(breq, DP_ERR_OK, EOK, "Success"); } - req = users_get_send(breq, breq->be_ctx->ev, ctx, + break; + + case BE_REQ_GROUP: /* group */ + + /* skip enumerations on demand */ + if (strcmp(ar->filter_value, "*") == 0) { + return sdap_handler_done(breq, DP_ERR_OK, EOK, "Success"); + } + + break; + + case BE_REQ_INITGROUPS: /* init groups for user */ + if (ar->filter_type != BE_FILTER_NAME) { + sdap_handler_done(breq, DP_ERR_FATAL, EINVAL, "Invalid filter type"); + return; + } + if (ar->attr_type != BE_ATTR_CORE) { + sdap_handler_done(breq, DP_ERR_FATAL, EINVAL, "Invalid attr type"); + return; + } + if (strchr(ar->filter_value, '*')) { + sdap_handler_done(breq, DP_ERR_FATAL, EINVAL, "Invalid filter value"); + return; + } + + break; + + default: /*fail*/ + sdap_handler_done(breq, DP_ERR_FATAL, EINVAL, "Invalid request type"); + return; + } + + state = sdap_account_info_create_state(ctx, breq); + if (!state) { + sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory"); + return; + } + + sdap_account_info_handler_retry(state); +} + +static struct sdap_account_info_state *sdap_account_info_create_state(struct sdap_id_ctx *ctx, struct be_req *breq) +{ + struct sdap_account_info_state* state = talloc_zero(breq, struct sdap_account_info_state); + if (!state) { + return NULL; + } + + state->breq = breq; + state->op = sdap_id_op_create(state, ctx); + if (!state->op) { + goto fail; + } + + return state; + +fail: + talloc_zfree(state); + return NULL; +} + +static void sdap_account_info_handler_retry(struct sdap_account_info_state* state) +{ + sdap_id_op_connect(state->op, sdap_account_request_connect_done, state); +} + +static void sdap_account_request_connect_done(struct sdap_id_op* op, int ret, void* pvt) +{ + struct sdap_account_info_state *state = talloc_get_type(pvt, + struct sdap_account_info_state); + struct be_req *breq = state->breq; + struct be_acct_req *ar; + struct tevent_req *req; + const char *err = "Unknown Error"; + + ar = talloc_get_type(breq->req_data, struct be_acct_req); + + switch (ar->entry_type & 0xFFF) { + case BE_REQ_USER: /* user */ + if (ret) { + err = "Enum Users Failed"; + break; + } + + req = users_get_send(breq, breq->be_ctx->ev, state->op, ar->filter_value, ar->filter_type, ar->attr_type); if (!req) { - return sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory"); + ret = ENOMEM; + break; } - tevent_req_set_callback(req, sdap_account_info_users_done, breq); + tevent_req_set_callback(req, sdap_account_info_users_done, state); break; case BE_REQ_GROUP: /* group */ - - if (strcmp(ar->filter_value, "*") == 0) { - return sdap_handler_done(breq, DP_ERR_OK, EOK, "Success"); + if (ret) { + err = "Enum Groups Failed"; + break; } - /* skip enumerations on demand */ - req = groups_get_send(breq, breq->be_ctx->ev, ctx, + req = groups_get_send(breq, breq->be_ctx->ev, state->op, ar->filter_value, ar->filter_type, ar->attr_type); if (!req) { - return sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory"); + ret = ENOMEM; + break; } - tevent_req_set_callback(req, sdap_account_info_groups_done, breq); + tevent_req_set_callback(req, sdap_account_info_groups_done, state); break; case BE_REQ_INITGROUPS: /* init groups for user */ - if (ar->filter_type != BE_FILTER_NAME) { - ret = EINVAL; - err = "Invalid filter type"; - break; - } - if (ar->attr_type != BE_ATTR_CORE) { - ret = EINVAL; - err = "Invalid attr type"; + if (ret) { + err = "Init Groups Failed"; break; } - if (strchr(ar->filter_value, '*')) { - ret = EINVAL; - err = "Invalid filter value"; + + req = groups_by_user_send(breq, breq->be_ctx->ev, state->op, + ar->filter_value); + if (!req) { + ret = ENOMEM; break; } - req = groups_by_user_send(breq, breq->be_ctx->ev, ctx, - ar->filter_value); - if (!req) ret = ENOMEM; - /* tevent_req_set_callback(req, groups_by_user_done, breq); */ - tevent_req_set_callback(req, sdap_account_info_initgr_done, breq); + tevent_req_set_callback(req, sdap_account_info_initgr_done, state); break; @@ -697,99 +634,66 @@ void sdap_account_info_handler(struct be_req *breq) err = "Invalid request type"; } - if (ret != EOK) return sdap_handler_done(breq, DP_ERR_FATAL, ret, err); + if (ret != EOK) { + talloc_zfree(state); + + if (ret == ENOMEM) { + err = "Out of memory"; + } + + sdap_handler_done(breq, DP_ERR_FATAL, ret, err); + } } -static void sdap_account_info_users_done(struct tevent_req *req) +static void sdap_account_request_done(struct tevent_req *req, int ret, const char* default_error) { - struct be_req *breq = tevent_req_callback_data(req, struct be_req); - struct sdap_id_ctx *ctx; - int dp_err = DP_ERR_OK; - const char *error = NULL; - int ret; + struct sdap_account_info_state *state = tevent_req_callback_data(req, struct sdap_account_info_state); + struct be_req *breq = state->breq; + int dp_err; + const char* error; - ret = users_get_recv(req); talloc_zfree(req); - if (ret) { - dp_err = DP_ERR_FATAL; - error = "Enum Users Failed"; - - if (ret == ETIMEDOUT || ret == EFAULT || ret == EIO) { - 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); + ret = sdap_id_op_done(state->op, ret, &dp_err); + switch (dp_err) { + case DP_ERR_OK: + if( ret != EOK) { + /* retry */ + sdap_account_info_handler_retry(state); return; } - sdap_mark_offline(ctx); - } + + error = NULL; + break; + + case DP_ERR_OFFLINE: + error = "Offline"; + break; + + default: + error = default_error; + break; } + talloc_zfree(state); sdap_handler_done(breq, dp_err, ret, error); } -static void sdap_account_info_groups_done(struct tevent_req *req) +static void sdap_account_info_users_done(struct tevent_req *req) { - struct be_req *breq = tevent_req_callback_data(req, struct be_req); - struct sdap_id_ctx *ctx; - int dp_err = DP_ERR_OK; - const char *error = NULL; - int ret; - - ret = groups_get_recv(req); - talloc_zfree(req); - - if (ret) { - dp_err = DP_ERR_FATAL; - error = "Enum Groups Failed"; - - if (ret == ETIMEDOUT || ret == EFAULT || ret == EIO) { - 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); - } - } + int ret = users_get_recv(req); + sdap_account_request_done(req, ret, "Enum Users Failed"); +} - return sdap_handler_done(breq, dp_err, ret, error); +static void sdap_account_info_groups_done(struct tevent_req *req) +{ + int ret = groups_get_recv(req); + sdap_account_request_done(req, ret, "Enum Groups Failed"); } static void sdap_account_info_initgr_done(struct tevent_req *req) { - struct be_req *breq = tevent_req_callback_data(req, struct be_req); - struct sdap_id_ctx *ctx; - int dp_err = DP_ERR_OK; - const char *error = NULL; - int ret; - - ret = groups_by_user_recv(req); - talloc_zfree(req); - - if (ret) { - dp_err = DP_ERR_FATAL; - error = "Init Groups Failed"; - - if (ret == ETIMEDOUT || ret == EFAULT || ret == EIO) { - 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); - } - } - - return sdap_handler_done(breq, dp_err, ret, error); + int ret = groups_by_user_recv(req); + sdap_account_request_done(req, ret, "Init Groups Failed"); } diff --git a/src/providers/ldap/ldap_id_enum.c b/src/providers/ldap/ldap_id_enum.c index bc06e8b..4e03d18 100644 --- a/src/providers/ldap/ldap_id_enum.c +++ b/src/providers/ldap/ldap_id_enum.c @@ -147,18 +147,19 @@ int ldap_id_enumerate_set_timer(struct sdap_id_ctx *ctx, struct timeval tv) struct global_enum_state { struct tevent_context *ev; struct sdap_id_ctx *ctx; + struct sdap_id_op *op; bool purge; }; static struct tevent_req *enum_users_send(TALLOC_CTX *memctx, struct tevent_context *ev, - struct sdap_id_ctx *ctx, + struct sdap_id_op *op, bool purge); static void ldap_id_enum_users_done(struct tevent_req *subreq); static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx, struct tevent_context *ev, - struct sdap_id_ctx *ctx, + struct sdap_id_op *op, bool purge); static void ldap_id_enum_groups_done(struct tevent_req *subreq); static void ldap_id_enum_cleanup_done(struct tevent_req *subreq); @@ -175,6 +176,11 @@ static struct tevent_req *ldap_id_enumerate_send(struct tevent_context *ev, state->ev = ev; state->ctx = ctx; + state->op = sdap_id_op_create(state, ctx); + if (!state->op) { + talloc_zfree(req); + return NULL; + } ctx->last_enum = tevent_timeval_current(); @@ -185,7 +191,7 @@ static struct tevent_req *ldap_id_enumerate_send(struct tevent_context *ev, state->purge = false; } - subreq = enum_users_send(state, ev, ctx, state->purge); + subreq = enum_users_send(state, ev, state->op, state->purge); if (!subreq) { talloc_zfree(req); return NULL; @@ -203,43 +209,54 @@ static void ldap_id_enum_users_done(struct tevent_req *subreq) struct global_enum_state); enum tevent_req_state tstate; uint64_t err = 0; + int retval, dp_err; if (tevent_req_is_error(subreq, &tstate, &err)) { if (tstate != TEVENT_REQ_USER_ERROR) { err = EIO; - } - if (err != ENOENT) { - goto fail; + } else if (err == ENOENT) { + err = EOK; } } + talloc_zfree(subreq); - subreq = enum_groups_send(state, state->ev, state->ctx, state->purge); - if (!subreq) { - goto fail; - } - tevent_req_set_callback(subreq, ldap_id_enum_groups_done, req); - - return; - -fail: - if (err) { - 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); + retval = sdap_id_op_done(state->op, err, &dp_err); + if (retval != EOK) { + if (dp_err == DP_ERR_OK) { + /* retry */ + subreq = enum_users_send(state, state->ev, state->op, state->purge); if (subreq != NULL) { tevent_req_set_callback(subreq, ldap_id_enum_users_done, req); return; } + + retval = ENOMEM; + dp_err = DP_ERR_FATAL; } - sdap_mark_offline(state->ctx); + + goto fail; + } + + subreq = enum_groups_send(state, state->ev, state->op, state->purge); + if (!subreq) { + retval = ENOMEM; + goto fail; } - DEBUG(1, ("Failed to enumerate users, retrying later!\n")); + tevent_req_set_callback(subreq, ldap_id_enum_groups_done, req); + return; + +fail: + if (dp_err != DP_ERR_OFFLINE) { + DEBUG(1, ("Failed to enumerate users (%d [%s]), retrying later!\n", + retval, strerror(retval))); + } else { + DEBUG(9, ("Enumerate users canceled due to Offline, retrying later!\n")); + } + tevent_req_done(req); + return; } static void ldap_id_enum_groups_done(struct tevent_req *subreq) @@ -250,21 +267,41 @@ static void ldap_id_enum_groups_done(struct tevent_req *subreq) struct global_enum_state); enum tevent_req_state tstate; uint64_t err = 0; + int retval, dp_err; if (tevent_req_is_error(subreq, &tstate, &err)) { if (tstate != TEVENT_REQ_USER_ERROR) { err = EIO; - } - if (err != ENOENT) { - goto fail; + } else if (err == ENOENT) { + err = EOK; } } + talloc_zfree(subreq); + retval = sdap_id_op_done(state->op, err, &dp_err); + if (retval != EOK) { + if (dp_err == DP_ERR_OK) { + /* retry */ + subreq = enum_groups_send(state, state->ev, state->op, state->purge); + if (subreq != NULL) { + tevent_req_set_callback(subreq, ldap_id_enum_groups_done, req); + return; + } + + retval = ENOMEM; + } + + goto fail; + } + + talloc_zfree(state->op); + if (state->purge) { subreq = ldap_id_cleanup_send(state, state->ev, state->ctx); if (!subreq) { + retval = ENOMEM; goto fail; } tevent_req_set_callback(subreq, ldap_id_enum_cleanup_done, req); @@ -274,21 +311,17 @@ static void ldap_id_enum_groups_done(struct tevent_req *subreq) tevent_req_done(req); return; - + fail: - /* 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; - } + if (dp_err != DP_ERR_OFFLINE) { + DEBUG(1, ("Failed to enumerate groups (%d [%s]), retrying later!\n", + retval, strerror(retval))); + } else { + DEBUG(9, ("Enumerate groups canceled due to Offline, retrying later!\n")); } - sdap_mark_offline(state->ctx); - DEBUG(1, ("Failed to enumerate groups (%d [%s]), retrying later!\n", - (int)err, strerror(err))); + tevent_req_done(req); + return; } static void ldap_id_enum_cleanup_done(struct tevent_req *subreq) @@ -310,16 +343,17 @@ struct enum_users_state { const char **attrs; }; -static void enum_users_connect_done(struct tevent_req *subreq); +static void enum_users_connect_done(struct sdap_id_op* op, int ret, void* pvt); static void enum_users_op_done(struct tevent_req *subreq); static struct tevent_req *enum_users_send(TALLOC_CTX *memctx, struct tevent_context *ev, - struct sdap_id_ctx *ctx, + struct sdap_id_op *op, bool purge) { - struct tevent_req *req, *subreq; + struct tevent_req *req; struct enum_users_state *state; + struct sdap_id_ctx *ctx = op->ctx; int ret; req = tevent_req_create(memctx, &state, struct enum_users_state); @@ -355,37 +389,11 @@ static struct tevent_req *enum_users_send(TALLOC_CTX *memctx, SDAP_OPTS_USER, &state->attrs); if (ret != EOK) goto fail; - if (!sdap_connected(ctx)) { - - if (ctx->gsh) talloc_zfree(ctx->gsh); - - /* FIXME: add option to decide if tls should be used - * or SASL/GSSAPI, etc ... */ - subreq = sdap_cli_connect_send(state, ev, ctx->opts, - ctx->be, ctx->service, - &ctx->rootDSE); - if (!subreq) { - ret = ENOMEM; - goto fail; - } - - tevent_req_set_callback(subreq, enum_users_connect_done, req); - - return req; - } - - subreq = sdap_get_users_send(state, state->ev, - state->ctx->be->domain, - state->ctx->be->sysdb, - state->ctx->opts, - state->ctx->gsh, - state->attrs, state->filter); - if (!subreq) { - ret = ENOMEM; - goto fail; + sdap_id_op_connect(op, enum_users_connect_done, req); + if (!tevent_req_is_in_progress(req)) { + tevent_req_post(req, ev); } - tevent_req_set_callback(subreq, enum_users_op_done, req); - + return req; fail: @@ -394,21 +402,14 @@ fail: return req; } -static void enum_users_connect_done(struct tevent_req *subreq) +static void enum_users_connect_done(struct sdap_id_op* op, int ret, void* pvt) { - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); struct enum_users_state *state = tevent_req_data(req, struct enum_users_state); - int ret; + struct tevent_req *subreq; - ret = sdap_cli_connect_recv(subreq, state->ctx, - &state->ctx->gsh, &state->ctx->rootDSE); - talloc_zfree(subreq); if (ret) { - if (ret == ENOTSUP) { - DEBUG(0, ("Authentication mechanism not Supported by server")); - } tevent_req_error(req, ret); return; } @@ -416,7 +417,7 @@ static void enum_users_connect_done(struct tevent_req *subreq) subreq = sdap_get_users_send(state, state->ev, state->ctx->be->domain, state->ctx->be->sysdb, - state->ctx->opts, state->ctx->gsh, + state->ctx->opts, sdap_id_op_handle(op), state->attrs, state->filter); if (!subreq) { tevent_req_error(req, ENOMEM); @@ -462,16 +463,17 @@ struct enum_groups_state { const char **attrs; }; -static void enum_groups_connect_done(struct tevent_req *subreq); +static void enum_groups_connect_done(struct sdap_id_op* op, int ret, void* pvt); static void enum_groups_op_done(struct tevent_req *subreq); static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx, struct tevent_context *ev, - struct sdap_id_ctx *ctx, + struct sdap_id_op *op, bool purge) { - struct tevent_req *req, *subreq; + struct tevent_req *req; struct enum_groups_state *state; + struct sdap_id_ctx *ctx = op->ctx; const char *attr_name; int ret; @@ -510,35 +512,10 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx, SDAP_OPTS_GROUP, &state->attrs); if (ret != EOK) goto fail; - if (!sdap_connected(ctx)) { - - if (ctx->gsh) talloc_zfree(ctx->gsh); - - /* FIXME: add option to decide if tls should be used - * or SASL/GSSAPI, etc ... */ - subreq = sdap_cli_connect_send(state, ev, ctx->opts, - ctx->be, ctx->service, - &ctx->rootDSE); - if (!subreq) { - ret = ENOMEM; - goto fail; - } - - tevent_req_set_callback(subreq, enum_groups_connect_done, req); - - return req; - } - - subreq = sdap_get_groups_send(state, state->ev, - state->ctx->be->domain, - state->ctx->be->sysdb, - state->ctx->opts, state->ctx->gsh, - state->attrs, state->filter); - if (!subreq) { - ret = ENOMEM; - goto fail; + sdap_id_op_connect(op, enum_groups_connect_done, req); + if (!tevent_req_is_in_progress(req)) { + tevent_req_post(req, ev); } - tevent_req_set_callback(subreq, enum_groups_op_done, req); return req; @@ -548,21 +525,14 @@ fail: return req; } -static void enum_groups_connect_done(struct tevent_req *subreq) +static void enum_groups_connect_done(struct sdap_id_op* op, int ret, void* pvt) { - struct tevent_req *req = tevent_req_callback_data(subreq, - struct tevent_req); + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); struct enum_groups_state *state = tevent_req_data(req, struct enum_groups_state); - int ret; + struct tevent_req *subreq; - ret = sdap_cli_connect_recv(subreq, state->ctx, - &state->ctx->gsh, &state->ctx->rootDSE); - talloc_zfree(subreq); if (ret) { - if (ret == ENOTSUP) { - DEBUG(0, ("Authentication mechanism not Supported by server")); - } tevent_req_error(req, ret); return; } @@ -570,7 +540,7 @@ static void enum_groups_connect_done(struct tevent_req *subreq) subreq = sdap_get_groups_send(state, state->ev, state->ctx->be->domain, state->ctx->be->sysdb, - state->ctx->opts, state->ctx->gsh, + state->ctx->opts, sdap_id_op_handle(op), state->attrs, state->filter); if (!subreq) { tevent_req_error(req, ENOMEM); diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h index f0e345e..a820ede 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -70,6 +70,8 @@ struct ldap_cb_data { struct sdap_handle { LDAP *ldap; bool connected; + /* Authentication ticket expiration time (if any) */ + time_t expire_time; #ifdef HAVE_LDAP_CONNCB struct ldap_conncb *conncb; diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h index 888df6b..7a14929 100644 --- a/src/providers/ldap/sdap_async.h +++ b/src/providers/ldap/sdap_async.h @@ -66,7 +66,7 @@ struct tevent_req *sdap_kinit_send(TALLOC_CTX *memctx, const char *keytab, const char *principal, const char *realm); -int sdap_kinit_recv(struct tevent_req *req, enum sdap_result *result); +int sdap_kinit_recv(struct tevent_req *req, enum sdap_result *result, time_t *expire_time); struct tevent_req *sdap_auth_send(TALLOC_CTX *memctx, struct tevent_context *ev, @@ -110,6 +110,7 @@ struct tevent_req *sdap_cli_connect_send(TALLOC_CTX *memctx, struct sysdb_attrs **rootdse); int sdap_cli_connect_recv(struct tevent_req *req, TALLOC_CTX *memctx, + bool *can_retry, struct sdap_handle **gsh, struct sysdb_attrs **rootdse); diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c index 2acbbb8..42fa45d 100644 --- a/src/providers/ldap/sdap_async_connection.c +++ b/src/providers/ldap/sdap_async_connection.c @@ -632,6 +632,7 @@ static int sasl_bind_recv(struct tevent_req *req, int *ldaperr) struct sdap_kinit_state { int result; + time_t expire_time; }; static void sdap_kinit_done(struct tevent_req *subreq); @@ -684,8 +685,9 @@ static void sdap_kinit_done(struct tevent_req *subreq) int ret; int result; char *ccname = NULL; + time_t expire_time; - ret = sdap_get_tgt_recv(subreq, state, &result, &ccname); + ret = sdap_get_tgt_recv(subreq, state, &result, &ccname, &expire_time); talloc_zfree(subreq); if (ret != EOK) { state->result = SDAP_AUTH_FAILED; @@ -702,6 +704,7 @@ static void sdap_kinit_done(struct tevent_req *subreq) tevent_req_error(req, EFAULT); } + state->expire_time = expire_time; state->result = SDAP_AUTH_SUCCESS; tevent_req_done(req); return; @@ -712,7 +715,7 @@ static void sdap_kinit_done(struct tevent_req *subreq) tevent_req_error(req, EIO); } -int sdap_kinit_recv(struct tevent_req *req, enum sdap_result *result) +int sdap_kinit_recv(struct tevent_req *req, enum sdap_result *result, time_t *expire_time) { struct sdap_kinit_state *state = tevent_req_data(req, struct sdap_kinit_state); @@ -727,6 +730,7 @@ int sdap_kinit_recv(struct tevent_req *req, enum sdap_result *result) } *result = state->result; + *expire_time = state->expire_time; return EOK; } @@ -1068,10 +1072,14 @@ static void sdap_cli_kinit_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); + struct sdap_cli_connect_state *state = tevent_req_data(req, + struct sdap_cli_connect_state); + enum sdap_result result; + time_t expire_time; int ret; - ret = sdap_kinit_recv(subreq, &result); + ret = sdap_kinit_recv(subreq, &result, &expire_time); talloc_zfree(subreq); if (ret) { tevent_req_error(req, ret); @@ -1081,6 +1089,7 @@ static void sdap_cli_kinit_done(struct tevent_req *subreq) tevent_req_error(req, EACCES); return; } + state->sh->expire_time = expire_time; sdap_cli_auth_step(req); } @@ -1134,6 +1143,7 @@ static void sdap_cli_auth_done(struct tevent_req *subreq) int sdap_cli_connect_recv(struct tevent_req *req, TALLOC_CTX *memctx, + bool *can_retry, struct sdap_handle **gsh, struct sysdb_attrs **rootdse) { @@ -1142,10 +1152,13 @@ int sdap_cli_connect_recv(struct tevent_req *req, enum tevent_req_state tstate; uint64_t err; + *can_retry = true; if (tevent_req_is_error(req, &tstate, &err)) { /* mark the server as bad if connection failed */ if (state->srv) { fo_set_port_status(state->srv, PORT_NOT_WORKING); + } else { + *can_retry = false; } if (tstate == TEVENT_REQ_USER_ERROR) { diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h index 55f76ed..a676364 100644 --- a/src/providers/ldap/sdap_async_private.h +++ b/src/providers/ldap/sdap_async_private.h @@ -63,6 +63,7 @@ struct tevent_req *sdap_get_tgt_send(TALLOC_CTX *mem_ctx, int sdap_get_tgt_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, int *result, - char **ccname); + char **ccname, + time_t *expire_time); #endif /* _SDAP_ASYNC_PRIVATE_H_ */ diff --git a/src/providers/ldap/sdap_child_helpers.c b/src/providers/ldap/sdap_child_helpers.c index 273fc67..af5c408 100644 --- a/src/providers/ldap/sdap_child_helpers.c +++ b/src/providers/ldap/sdap_child_helpers.c @@ -30,6 +30,7 @@ #include "util/util.h" #include "providers/ldap/ldap_common.h" +#include "providers/ldap/sdap_async_private.h" #include "providers/child_common.h" #ifndef SSSD_LIBEXEC_PATH @@ -194,12 +195,15 @@ static errno_t create_tgt_req_send_buffer(TALLOC_CTX *mem_ctx, static int parse_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf, ssize_t size, - int *result, char **ccache) + int *result, char **ccache, + time_t *expire_time_out) { size_t p = 0; uint32_t len; uint32_t res; - char *ccn; + char *ccn = 0; + time_t expire_time; + int retval = EOK; /* operation result code */ SAFEALIGN_COPY_UINT32_CHECK(&res, buf + p, size, &p); @@ -207,19 +211,34 @@ static int parse_child_response(TALLOC_CTX *mem_ctx, /* ccache name size */ SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p); - if ((p + len ) > size) return EINVAL; - + if ((p + len ) > size) + return EINVAL; + ccn = talloc_size(mem_ctx, sizeof(char) * (len + 1)); if (ccn == NULL) { DEBUG(1, ("talloc_size failed.\n")); return ENOMEM; } - memcpy(ccn, buf+p, sizeof(char) * (len + 1)); + safealign_memcpy(ccn, buf+p, sizeof(char) * len, &p); ccn[len] = '\0'; + + if (p + sizeof(time_t) > size) { + retval = EINVAL; + goto cleanup; + } + safealign_memcpy(&expire_time, buf+p, sizeof(time_t), &p); *result = res; *ccache = ccn; - return EOK; + *expire_time_out = expire_time; + return retval; + +cleanup: + if (ccn != 0) { + talloc_free(ccn); + } + + return retval; } /* ==The-public-async-interface============================================*/ @@ -352,25 +371,28 @@ static void sdap_get_tgt_done(struct tevent_req *subreq) int sdap_get_tgt_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, int *result, - char **ccname) + char **ccname, + time_t *expire_time_out) { struct sdap_get_tgt_state *state = tevent_req_data(req, struct sdap_get_tgt_state); char *ccn; + time_t expire_time; int res; int ret; TEVENT_REQ_RETURN_ON_ERROR(req); - ret = parse_child_response(mem_ctx, state->buf, state->len, &res, &ccn); + ret = parse_child_response(mem_ctx, state->buf, state->len, &res, &ccn, &expire_time); if (ret != EOK) { DEBUG(1, ("Cannot parse child response: [%d][%s]\n", ret, strerror(ret))); return ret; } - DEBUG(6, ("Child responded: %d [%s]\n", res, ccn)); + DEBUG(6, ("Child responded: %d [%s], expired on [%ld]\n", res, ccn, (long)expire_time)); *result = res; *ccname = ccn; + *expire_time_out = expire_time; return EOK; }