From dd6bf2b53c0f01cf5d0e748ae090cefa0557990c Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Wed, 26 Aug 2009 16:52:18 -0400 Subject: Make enumeration an independent task Always immediately return to DP, and update users/groups in the background. Also implements an optimization to retrieve only changed/new users/groups by filtering using the modifyTimestamp after the first query. --- server/providers/ldap/ldap_id.c | 575 ++++++++++++++++++++++++++++++++++--- server/providers/ldap/sdap.c | 22 +- server/providers/ldap/sdap.h | 10 +- server/providers/ldap/sdap_async.c | 148 +++++++++- server/providers/ldap/sdap_async.h | 6 +- 5 files changed, 693 insertions(+), 68 deletions(-) (limited to 'server') diff --git a/server/providers/ldap/ldap_id.c b/server/providers/ldap/ldap_id.c index a80933084..b5fdc63bd 100644 --- a/server/providers/ldap/ldap_id.c +++ b/server/providers/ldap/ldap_id.c @@ -32,6 +32,8 @@ #include "providers/ldap/sdap_async.h" struct sdap_id_ctx { + struct be_ctx *be; + struct sdap_options *opts; /* global sdap handler */ @@ -39,6 +41,12 @@ struct sdap_id_ctx { time_t went_offline; bool offline; + + /* enumeration loop timer */ + struct timeval last_run; + + char *max_user_timestamp; + char *max_group_timestamp; }; static void sdap_req_done(struct be_req *req, int ret, const char *err) @@ -79,20 +87,23 @@ static int build_attrs_from_map(TALLOC_CTX *memctx, const char ***_attrs) { char **attrs; - int i; + int i, j; attrs = talloc_array(memctx, char *, size + 1); if (!attrs) return ENOMEM; - /* first attribute is the objectclass value not the attribute name */ + /* first attribute is "objectclass" not the specifc one */ attrs[0] = talloc_strdup(memctx, "objectClass"); if (!attrs[0]) return ENOMEM; /* add the others */ - for (i = 1; i < size; i++) { - attrs[i] = map[i].name; + for (i = j = 1; i < size; i++) { + if (map[i].name) { + attrs[j] = map[i].name; + j++; + } } - attrs[i] = NULL; + attrs[j] = NULL; *_attrs = (const char **)attrs; @@ -229,9 +240,6 @@ struct users_get_state { struct tevent_context *ev; struct sdap_id_ctx *ctx; - struct sss_domain_info *dom; - struct sysdb_ctx *sysdb; - char *filter; const char **attrs; }; @@ -242,8 +250,7 @@ static void users_get_op_done(struct tevent_req *subreq); static struct tevent_req *users_get_send(TALLOC_CTX *memctx, struct tevent_context *ev, struct sdap_id_ctx *ctx, - struct be_ctx *bectx, - const char *wildcard, + const char *name, int filter_type, int attrs_type) { @@ -257,8 +264,6 @@ static struct tevent_req *users_get_send(TALLOC_CTX *memctx, state->ev = ev; state->ctx = ctx; - state->dom = bectx->domain; - state->sysdb = bectx->sysdb; switch(filter_type) { case BE_FILTER_NAME: @@ -273,7 +278,7 @@ static struct tevent_req *users_get_send(TALLOC_CTX *memctx, } state->filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))", - attr_name, wildcard, + attr_name, name, ctx->opts->user_map[SDAP_OC_USER].name); if (!state->filter) { DEBUG(2, ("Failed to build filter\n")); @@ -307,7 +312,8 @@ static struct tevent_req *users_get_send(TALLOC_CTX *memctx, } subreq = sdap_get_users_send(state, state->ev, - state->dom, bectx->sysdb, + state->ctx->be->domain, + state->ctx->be->sysdb, state->ctx->opts, state->ctx->gsh, state->attrs, state->filter); if (!subreq) { @@ -340,7 +346,8 @@ static void users_get_connect_done(struct tevent_req *subreq) } subreq = sdap_get_users_send(state, state->ev, - state->dom, state->sysdb, + state->ctx->be->domain, + state->ctx->be->sysdb, state->ctx->opts, state->ctx->gsh, state->attrs, state->filter); if (!subreq) { @@ -356,7 +363,7 @@ static void users_get_op_done(struct tevent_req *subreq) struct tevent_req); int ret; - ret = sdap_get_users_recv(subreq); + ret = sdap_get_users_recv(subreq, NULL, NULL); talloc_zfree(subreq); if (ret) { tevent_req_error(req, ret); @@ -389,9 +396,6 @@ struct groups_get_state { struct tevent_context *ev; struct sdap_id_ctx *ctx; - struct sss_domain_info *dom; - struct sysdb_ctx *sysdb; - char *filter; const char **attrs; }; @@ -402,8 +406,7 @@ static void groups_get_op_done(struct tevent_req *subreq); static struct tevent_req *groups_get_send(TALLOC_CTX *memctx, struct tevent_context *ev, struct sdap_id_ctx *ctx, - struct be_ctx *bectx, - const char *wildcard, + const char *name, int filter_type, int attrs_type) { @@ -417,8 +420,6 @@ static struct tevent_req *groups_get_send(TALLOC_CTX *memctx, state->ev = ev; state->ctx = ctx; - state->dom = bectx->domain; - state->sysdb = bectx->sysdb; switch(filter_type) { case BE_FILTER_NAME: @@ -433,7 +434,7 @@ static struct tevent_req *groups_get_send(TALLOC_CTX *memctx, } state->filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))", - attr_name, wildcard, + attr_name, name, ctx->opts->group_map[SDAP_OC_GROUP].name); if (!state->filter) { DEBUG(2, ("Failed to build filter\n")); @@ -467,7 +468,8 @@ static struct tevent_req *groups_get_send(TALLOC_CTX *memctx, } subreq = sdap_get_groups_send(state, state->ev, - state->dom, bectx->sysdb, + state->ctx->be->domain, + state->ctx->be->sysdb, state->ctx->opts, state->ctx->gsh, state->attrs, state->filter); if (!subreq) { @@ -500,9 +502,10 @@ static void groups_get_connect_done(struct tevent_req *subreq) } subreq = sdap_get_groups_send(state, state->ev, - state->dom, state->sysdb, - state->ctx->opts, state->ctx->gsh, - state->attrs, state->filter); + state->ctx->be->domain, + state->ctx->be->sysdb, + state->ctx->opts, state->ctx->gsh, + state->attrs, state->filter); if (!subreq) { tevent_req_error(req, ENOMEM); return; @@ -516,7 +519,7 @@ static void groups_get_op_done(struct tevent_req *subreq) struct tevent_req); int ret; - ret = sdap_get_groups_recv(subreq); + ret = sdap_get_groups_recv(subreq, NULL, NULL); talloc_zfree(subreq); if (ret) { tevent_req_error(req, ret); @@ -548,8 +551,6 @@ static void groups_get_done(struct tevent_req *req) struct groups_by_user_state { struct tevent_context *ev; struct sdap_id_ctx *ctx; - struct sss_domain_info *dom; - struct sysdb_ctx *sysdb; const char *name; const char **attrs; }; @@ -560,8 +561,6 @@ static void groups_by_user_op_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 sss_domain_info *dom, - struct sysdb_ctx *sysdb, const char *name) { struct tevent_req *req, *subreq; @@ -573,8 +572,6 @@ static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx, state->ev = ev; state->ctx = ctx; - state->dom = dom; - state->sysdb = sysdb; state->name = name; ret = build_attrs_from_map(state, ctx->opts->group_map, @@ -602,7 +599,8 @@ static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx, } subreq = sdap_get_initgr_send(state, state->ev, - state->dom, state->sysdb, + state->ctx->be->domain, + state->ctx->be->sysdb, state->ctx->opts, state->ctx->gsh, state->name, state->attrs); if (!subreq) { @@ -635,7 +633,8 @@ static void groups_by_user_connect_done(struct tevent_req *subreq) } subreq = sdap_get_initgr_send(state, state->ev, - state->dom, state->sysdb, + state->ctx->be->domain, + state->ctx->be->sysdb, state->ctx->opts, state->ctx->gsh, state->name, state->attrs); if (!subreq) { @@ -703,8 +702,12 @@ static void sdap_get_account_info(struct be_req *breq) switch (ar->entry_type) { case BE_REQ_USER: /* user */ - req = users_get_send(breq, breq->be_ctx->ev, - ctx, breq->be_ctx, + /* skip enumerations on demand */ + if (strcmp(ar->filter_value, "*") == 0) { + return sdap_req_done(breq, EOK, "Success"); + } + + req = users_get_send(breq, breq->be_ctx->ev, ctx, ar->filter_value, ar->filter_type, ar->attr_type); @@ -718,8 +721,12 @@ static void sdap_get_account_info(struct be_req *breq) case BE_REQ_GROUP: /* group */ - req = groups_get_send(breq, breq->be_ctx->ev, - ctx, breq->be_ctx, + if (strcmp(ar->filter_value, "*") == 0) { + return sdap_req_done(breq, EOK, "Success"); + } + + /* skip enumerations on demand */ + req = groups_get_send(breq, breq->be_ctx->ev, ctx, ar->filter_value, ar->filter_type, ar->attr_type); @@ -748,7 +755,6 @@ static void sdap_get_account_info(struct be_req *breq) break; } req = groups_by_user_send(breq, breq->be_ctx->ev, ctx, - breq->be_ctx->domain, breq->be_ctx->sysdb, ar->filter_value); if (!req) ret = ENOMEM; /* tevent_req_set_callback(req, groups_by_user_done, breq); */ @@ -765,6 +771,478 @@ static void sdap_get_account_info(struct be_req *breq) if (ret != EOK) return sdap_req_done(breq, ret, err); } + +/* ==Enumeration-Task===================================================== */ + +static struct tevent_req *ldap_id_enumerate_send(struct tevent_context *ev, + struct sdap_id_ctx *ctx); +static void ldap_id_enumerate_reschedule(struct tevent_req *req); +static void ldap_id_enumerate_set_timer(struct sdap_id_ctx *ctx, + struct timeval tv); + +static void ldap_id_enumerate_timeout(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt); + +static void ldap_id_enumerate(struct tevent_context *ev, + struct tevent_timer *tt, + struct timeval tv, void *pvt) +{ + struct sdap_id_ctx *ctx = talloc_get_type(pvt, struct sdap_id_ctx); + struct tevent_timer *timeout; + struct tevent_req *req; + + ctx->last_run = tv; + + req = ldap_id_enumerate_send(ev, ctx); + if (!req) { + DEBUG(1, ("Failed to schedule enumeration, retrying later!\n")); + /* schedule starting from now, not the last run */ + ldap_id_enumerate_set_timer(ctx, tevent_timeval_current()); + return; + } + + tevent_req_set_callback(req, ldap_id_enumerate_reschedule, ctx); + + /* if enumeration takes so long, either we try to enumerate too + * frequently, or something went seriously wrong */ + tv = tevent_timeval_current(); + tv = tevent_timeval_add(&tv, ctx->opts->enum_refresh_timeout, 0); + timeout = tevent_add_timer(ctx->be->ev, req, tv, + ldap_id_enumerate_timeout, req); + return; +} + +static void ldap_id_enumerate_timeout(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct sdap_id_ctx *ctx = tevent_req_callback_data(req, + struct sdap_id_ctx); + + DEBUG(1, ("Enumeration timed out! Timeout too small? (%ds)!\n", + ctx->opts->enum_refresh_timeout)); + ldap_id_enumerate_set_timer(ctx, tevent_timeval_current()); + + talloc_zfree(req); +} + +static void ldap_id_enumerate_reschedule(struct tevent_req *req) +{ + struct sdap_id_ctx *ctx = tevent_req_callback_data(req, + struct sdap_id_ctx); + ldap_id_enumerate_set_timer(ctx, ctx->last_run); +} + +static void ldap_id_enumerate_set_timer(struct sdap_id_ctx *ctx, + struct timeval tv) +{ + struct tevent_timer *enum_task; + + tv = tevent_timeval_add(&tv, ctx->opts->enum_refresh_timeout, 0); + enum_task = tevent_add_timer(ctx->be->ev, ctx, tv, ldap_id_enumerate, ctx); + if (!enum_task) { + DEBUG(0, ("FATAL: failed to setup enumeration task!\n")); + /* shutdown! */ + exit(1); + } +} + + + +struct global_enum_state { + struct tevent_context *ev; + struct sdap_id_ctx *ctx; +}; + +static struct tevent_req *enum_users_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_id_ctx *ctx); +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); +static void ldap_id_enum_groups_done(struct tevent_req *subreq); + +static struct tevent_req *ldap_id_enumerate_send(struct tevent_context *ev, + struct sdap_id_ctx *ctx) +{ + struct global_enum_state *state; + struct tevent_req *req, *subreq; + + req = tevent_req_create(ctx, &state, struct global_enum_state); + if (!req) return NULL; + + state->ev = ev; + state->ctx = ctx; + + subreq = enum_users_send(state, ev, ctx); + if (!subreq) { + talloc_zfree(req); + return NULL; + } + tevent_req_set_callback(subreq, ldap_id_enum_users_done, req); + + return req; +} + +static void ldap_id_enum_users_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct global_enum_state *state = tevent_req_data(req, + struct global_enum_state); + enum tevent_req_state tstate; + uint64_t err; + + if (tevent_req_is_error(subreq, &tstate, &err)) { + goto fail; + } + talloc_zfree(subreq); + + subreq = enum_groups_send(state, state->ev, state->ctx); + if (!subreq) { + goto fail; + } + tevent_req_set_callback(subreq, ldap_id_enum_groups_done, req); + + return; + +fail: + DEBUG(1, ("Failed to enumerate users, retrying later!\n")); + /* schedule starting from now, not the last run */ + ldap_id_enumerate_set_timer(state->ctx, tevent_timeval_current()); + + talloc_zfree(req); +} + +static void ldap_id_enum_groups_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct global_enum_state *state = tevent_req_data(req, + struct global_enum_state); + enum tevent_req_state tstate; + uint64_t err; + + if (tevent_req_is_error(subreq, &tstate, &err)) { + goto fail; + } + talloc_zfree(req); + + ldap_id_enumerate_set_timer(state->ctx, state->ctx->last_run); + + return; + +fail: + DEBUG(1, ("Failed to enumerate groups, retrying later!\n")); + /* schedule starting from now, not the last run */ + ldap_id_enumerate_set_timer(state->ctx, tevent_timeval_current()); + + talloc_zfree(req); +} + +/* ==User-Enumeration===================================================== */ + +struct enum_users_state { + struct tevent_context *ev; + struct sdap_id_ctx *ctx; + + char *filter; + const char **attrs; +}; + +static void enum_users_connect_done(struct tevent_req *subreq); +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 tevent_req *req, *subreq; + struct users_get_state *state; + int ret; + + req = tevent_req_create(memctx, &state, struct users_get_state); + if (!req) return NULL; + + state->ev = ev; + state->ctx = ctx; + + if (ctx->max_user_timestamp) { + state->filter = talloc_asprintf(state, + "(&(%s=*)(objectclass=%s)(%s>=%s)(!(%s=%s)))", + ctx->opts->user_map[SDAP_AT_USER_NAME].name, + ctx->opts->user_map[SDAP_OC_USER].name, + ctx->opts->user_map[SDAP_AT_USER_MODSTAMP].name, + ctx->max_user_timestamp, + ctx->opts->user_map[SDAP_AT_USER_MODSTAMP].name, + ctx->max_user_timestamp); + } else { + state->filter = talloc_asprintf(state, + "(&(%s=*)(objectclass=%s))", + ctx->opts->user_map[SDAP_AT_USER_NAME].name, + ctx->opts->user_map[SDAP_OC_USER].name); + } + if (!state->filter) { + DEBUG(2, ("Failed to build filter\n")); + ret = ENOMEM; + goto fail; + } + + /* TODO: handle attrs_type */ + ret = build_attrs_from_map(state, ctx->opts->user_map, + SDAP_OPTS_USER, &state->attrs); + if (ret != EOK) goto fail; + + if (!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_id_connect_send(state, ev, ctx, false, + ctx->opts->basic[SDAP_DEFAULT_BIND_DN].value, + ctx->opts->basic[SDAP_DEFAULT_AUTHTOK_TYPE].value, + ctx->opts->basic[SDAP_DEFAULT_AUTHTOK].value); + 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; + } + tevent_req_set_callback(subreq, enum_users_op_done, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void enum_users_connect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct enum_users_state *state = tevent_req_data(req, + struct enum_users_state); + int ret; + + ret = sdap_id_connect_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + 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) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, enum_users_op_done, req); +} + +static void enum_users_op_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct enum_users_state *state = tevent_req_data(req, + struct enum_users_state); + char *timestamp; + int ret; + + ret = sdap_get_users_recv(subreq, state, ×tamp); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (timestamp) { + talloc_zfree(state->ctx->max_user_timestamp); + state->ctx->max_user_timestamp = talloc_steal(state->ctx, timestamp); + } + + DEBUG(4, ("Users higher timestamp: [%s]\n", + state->ctx->max_user_timestamp)); + + tevent_req_done(req); +} + +/* =Group-Enumeration===================================================== */ + +struct enum_groups_state { + struct tevent_context *ev; + struct sdap_id_ctx *ctx; + + char *filter; + const char **attrs; +}; + +static void enum_groups_connect_done(struct tevent_req *subreq); +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 tevent_req *req, *subreq; + struct enum_groups_state *state; + const char *attr_name; + int ret; + + req = tevent_req_create(memctx, &state, struct enum_groups_state); + if (!req) return NULL; + + state->ev = ev; + state->ctx = ctx; + + attr_name = ctx->opts->group_map[SDAP_AT_GROUP_NAME].name; + + if (ctx->max_group_timestamp) { + state->filter = talloc_asprintf(state, + "(&(%s=*)(objectclass=%s)(%s>=%s)(!(%s=%s)))", + ctx->opts->group_map[SDAP_AT_GROUP_NAME].name, + ctx->opts->group_map[SDAP_OC_GROUP].name, + ctx->opts->group_map[SDAP_AT_GROUP_MODSTAMP].name, + ctx->max_group_timestamp, + ctx->opts->group_map[SDAP_AT_GROUP_MODSTAMP].name, + ctx->max_group_timestamp); + } else { + state->filter = talloc_asprintf(state, + "(&(%s=*)(objectclass=%s))", + ctx->opts->group_map[SDAP_AT_GROUP_NAME].name, + ctx->opts->group_map[SDAP_OC_GROUP].name); + } + if (!state->filter) { + DEBUG(2, ("Failed to build filter\n")); + ret = ENOMEM; + goto fail; + } + + /* TODO: handle attrs_type */ + ret = build_attrs_from_map(state, ctx->opts->group_map, + SDAP_OPTS_GROUP, &state->attrs); + if (ret != EOK) goto fail; + + if (!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_id_connect_send(state, ev, ctx, false, + ctx->opts->basic[SDAP_DEFAULT_BIND_DN].value, + ctx->opts->basic[SDAP_DEFAULT_AUTHTOK_TYPE].value, + ctx->opts->basic[SDAP_DEFAULT_AUTHTOK].value); + 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; + } + tevent_req_set_callback(subreq, enum_groups_op_done, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void enum_groups_connect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct enum_groups_state *state = tevent_req_data(req, + struct enum_groups_state); + int ret; + + ret = sdap_id_connect_recv(subreq); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + 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) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, enum_groups_op_done, req); +} + +static void enum_groups_op_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct enum_groups_state *state = tevent_req_data(req, + struct enum_groups_state); + char *timestamp; + int ret; + + ret = sdap_get_groups_recv(subreq, state, ×tamp); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + if (timestamp) { + talloc_zfree(state->ctx->max_group_timestamp); + state->ctx->max_group_timestamp = talloc_steal(state->ctx, timestamp); + } + + DEBUG(4, ("Groups higher timestamp: [%s]\n", + state->ctx->max_group_timestamp)); + + tevent_req_done(req); +} + + + +/* ==Initialization-Functions============================================= */ + static void sdap_shutdown(struct be_req *req) { /* TODO: Clean up any internal data */ @@ -782,6 +1260,7 @@ int sssm_ldap_init(struct be_ctx *bectx, void **pvt_data) { int ldap_opt_x_tls_require_cert; + struct tevent_timer *enum_task; struct sdap_id_ctx *ctx; char *tls_reqcert; int ret; @@ -789,6 +1268,8 @@ int sssm_ldap_init(struct be_ctx *bectx, ctx = talloc_zero(bectx, struct sdap_id_ctx); if (!ctx) return ENOMEM; + ctx->be = bectx; + ret = sdap_get_options(ctx, bectx->cdb, bectx->conf_path, &ctx->opts); @@ -825,6 +1306,16 @@ int sssm_ldap_init(struct be_ctx *bectx, } } + /* set up enumeration task */ + ctx->last_run = tevent_timeval_current(); /* run the first immediately */ + enum_task = tevent_add_timer(ctx->be->ev, ctx, ctx->last_run, + ldap_id_enumerate, ctx); + if (!enum_task) { + DEBUG(0, ("FATAL: failed to setup enumeration task!\n")); + ret = EFAULT; + goto done; + } + *ops = &sdap_id_ops; *pvt_data = ctx; ret = EOK; diff --git a/server/providers/ldap/sdap.c b/server/providers/ldap/sdap.c index 3260043bb..dacce9c20 100644 --- a/server/providers/ldap/sdap.c +++ b/server/providers/ldap/sdap.c @@ -40,8 +40,9 @@ struct sdap_gen_opts default_basic_opts[] = { { "groupSearchScope", "sub", NULL }, { "groupSearchFilter", NULL, NULL }, { "ldapSchema", "rfc2307", NULL }, - { "offline_timeout", "5", NULL }, - { "force_upper_case_realm", "0", NULL } + { "offline_timeout", "60", NULL }, + { "force_upper_case_realm", "0", NULL }, + { "enumeration_refresh_timeout", "300", NULL } }; struct sdap_id_map rfc2307_user_map[] = { @@ -56,7 +57,8 @@ struct sdap_id_map rfc2307_user_map[] = { { "userPrincipal", "krbPrincipalName", SYSDB_UPN, NULL }, { "userFullname", "cn", SYSDB_FULLNAME, NULL }, { "userMemberOf", NULL, SYSDB_MEMBEROF, NULL }, - { "userUUID", NULL, SYSDB_UUID, NULL } + { "userUUID", NULL, SYSDB_UUID, NULL }, + { "userModifyTimestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL } }; struct sdap_id_map rfc2307_group_map[] = { @@ -65,7 +67,8 @@ struct sdap_id_map rfc2307_group_map[] = { { "groupPwd", "userPassword", SYSDB_PWD, NULL }, { "groupGidNumber", "gidNumber", SYSDB_GIDNUM, NULL }, { "groupMember", "memberuid", SYSDB_MEMBER, NULL }, - { "groupUUID", NULL, SYSDB_UUID, NULL } + { "groupUUID", NULL, SYSDB_UUID, NULL }, + { "groupModifyTimestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL } }; struct sdap_id_map rfc2307bis_user_map[] = { @@ -81,7 +84,8 @@ struct sdap_id_map rfc2307bis_user_map[] = { { "userFullname", "cn", SYSDB_FULLNAME, NULL }, { "userMemberOf", "memberOf", SYSDB_MEMBEROF, NULL }, /* FIXME: this is 389ds specific */ - { "userUUID", "nsUniqueId", SYSDB_UUID, NULL } + { "userUUID", "nsUniqueId", SYSDB_UUID, NULL }, + { "userModifyTimestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL } }; struct sdap_id_map rfc2307bis_group_map[] = { @@ -91,7 +95,8 @@ struct sdap_id_map rfc2307bis_group_map[] = { { "groupGidNumber", "gidNumber", SYSDB_GIDNUM, NULL }, { "groupMember", "member", SYSDB_MEMBER, NULL }, /* FIXME: this is 389ds specific */ - { "groupUUID", "nsUniqueId", SYSDB_UUID, NULL } + { "groupUUID", "nsUniqueId", SYSDB_UUID, NULL }, + { "groupModifyTimestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL } }; /* =Retrieve-Options====================================================== */ @@ -171,6 +176,11 @@ int sdap_get_options(TALLOC_CTX *memctx, &opts->force_upper_case_realm); if (ret != EOK) goto done; + ret = confdb_get_int(cdb, opts, conf_path, + "enumeration_refresh_timeout", 300, + &opts->enum_refresh_timeout); + if (ret != EOK) goto done; + /* schema type */ if (strcasecmp(opts->basic[SDAP_SCHEMA].value, "rfc2307") == 0) { opts->schema_type = SDAP_SCHEMA_RFC2307; diff --git a/server/providers/ldap/sdap.h b/server/providers/ldap/sdap.h index 50fc3d109..42af68f5a 100644 --- a/server/providers/ldap/sdap.h +++ b/server/providers/ldap/sdap.h @@ -85,8 +85,9 @@ enum sdap_result { #define SDAP_SCHEMA 13 #define SDAP_OFFLINE_TIMEOUT 14 #define SDAP_FORCE_UPPER_CASE_REALM 15 +#define SDAP_ENUM_REFRESH_TIMEOUT 16 -#define SDAP_OPTS_BASIC 16 /* opts counter */ +#define SDAP_OPTS_BASIC 17 /* opts counter */ /* the objectclass must be the first attribute. * Functions depend on this */ @@ -102,8 +103,9 @@ enum sdap_result { #define SDAP_AT_USER_PRINC 9 #define SDAP_AT_USER_FULLNAME 10 #define SDAP_AT_USER_MEMBEROF 11 +#define SDAP_AT_USER_MODSTAMP 12 -#define SDAP_OPTS_USER 12 /* attrs counter */ +#define SDAP_OPTS_USER 13 /* attrs counter */ /* the objectclass must be the first attribute. * Functions depend on this */ @@ -113,8 +115,9 @@ enum sdap_result { #define SDAP_AT_GROUP_GID 3 #define SDAP_AT_GROUP_MEMBER 4 #define SDAP_AT_GROUP_UUID 5 +#define SDAP_AT_GROUP_MODSTAMP 6 -#define SDAP_OPTS_GROUP 6 /* attrs counter */ +#define SDAP_OPTS_GROUP 7 /* attrs counter */ struct sdap_gen_opts { const char *opt_name; @@ -140,6 +143,7 @@ struct sdap_options { int network_timeout; int opt_timeout; int offline_timeout; + int enum_refresh_timeout; bool force_upper_case_realm; /* supported schema types */ diff --git a/server/providers/ldap/sdap_async.c b/server/providers/ldap/sdap_async.c index 57ccd8651..4d74061f1 100644 --- a/server/providers/ldap/sdap_async.c +++ b/server/providers/ldap/sdap_async.c @@ -834,6 +834,7 @@ struct sdap_save_user_state { const char *name; struct sysdb_attrs *attrs; + char *timestamp; }; static void sdap_save_user_done(struct tevent_req *subreq); @@ -871,6 +872,7 @@ static struct tevent_req *sdap_save_user_send(TALLOC_CTX *memctx, state->sh = sh; state->dom = dom; state->opts = opts; + state->timestamp = NULL; ret = sdap_parse_user(state, state->opts, state->sh, entry, &state->attrs, NULL); @@ -965,6 +967,29 @@ static struct tevent_req *sdap_save_user_send(TALLOC_CTX *memctx, } } + ret = sysdb_attrs_get_el(state->attrs, + opts->user_map[SDAP_AT_USER_MODSTAMP].sys_name, &el); + if (ret) { + goto fail; + } + if (el->num_values == 0) { + DEBUG(7, ("Original mod-Timestamp is not available for [%s].\n", + state->name)); + } else { + ret = sysdb_attrs_add_string(user_attrs, + opts->user_map[SDAP_AT_USER_MODSTAMP].sys_name, + (const char*)el->values[0].data); + if (ret) { + goto fail; + } + state->timestamp = talloc_strdup(state, + (const char*)el->values[0].data); + if (!state->timestamp) { + ret = ENOMEM; + goto fail; + } + } + ret = sysdb_attrs_get_el(state->attrs, opts->user_map[SDAP_AT_USER_PRINC].sys_name, &el); if (ret) { @@ -991,10 +1016,10 @@ static struct tevent_req *sdap_save_user_send(TALLOC_CTX *memctx, DEBUG(6, ("Storing info for user %s\n", state->name)); - subreq = sysdb_store_user_with_attrs_send(state, state->ev, state->handle, - state->dom, state->name, pwd, - uid, gid, gecos, homedir, shell, - user_attrs); + subreq = sysdb_store_user_send(state, state->ev, state->handle, + state->dom, state->name, pwd, + uid, gid, gecos, homedir, shell, + user_attrs); if (!subreq) { ret = ENOMEM; goto fail; @@ -1017,7 +1042,7 @@ static void sdap_save_user_done(struct tevent_req *subreq) struct sdap_save_user_state); int ret; - ret = sysdb_store_user_with_attrs_recv(subreq); + ret = sysdb_store_user_recv(subreq); talloc_zfree(subreq); if (ret) { DEBUG(2, ("Failed to save user %s\n", state->name)); @@ -1028,8 +1053,11 @@ static void sdap_save_user_done(struct tevent_req *subreq) tevent_req_done(req); } -int sdap_save_user_recv(struct tevent_req *req) +static int sdap_save_user_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, char **timestamp) { + struct sdap_save_user_state *state = tevent_req_data(req, + struct sdap_save_user_state); enum tevent_req_state tstate; uint64_t err; @@ -1038,6 +1066,8 @@ int sdap_save_user_recv(struct tevent_req *req) return err; } + *timestamp = talloc_steal(mem_ctx, state->timestamp); + return EOK; } @@ -1054,6 +1084,7 @@ struct sdap_save_group_state { const char *name; struct sysdb_attrs *attrs; + char *timestamp; }; static void sdap_save_group_done(struct tevent_req *subreq); @@ -1076,6 +1107,7 @@ static struct tevent_req *sdap_save_group_send(TALLOC_CTX *memctx, const char **members; long int l; gid_t gid; + struct sysdb_attrs *group_attrs; req = tevent_req_create(memctx, &state, struct sdap_save_group_state); if (!req) return NULL; @@ -1085,6 +1117,7 @@ static struct tevent_req *sdap_save_group_send(TALLOC_CTX *memctx, state->sh = sh; state->dom = dom; state->opts = opts; + state->timestamp = NULL; ret = sdap_parse_group(state, state->opts, state->sh, entry, &state->attrs, NULL); @@ -1126,10 +1159,41 @@ static struct tevent_req *sdap_save_group_send(TALLOC_CTX *memctx, } gid = l; + group_attrs = sysdb_new_attrs(state); + if (!group_attrs) { + ret = ENOMEM; + goto fail; + } + + ret = sysdb_attrs_get_el(state->attrs, + opts->group_map[SDAP_AT_GROUP_MODSTAMP].sys_name, &el); + if (ret) { + goto fail; + } + if (el->num_values == 0) { + DEBUG(7, ("Original mod-Timestamp is not available for [%s].\n", + state->name)); + } else { + ret = sysdb_attrs_add_string(group_attrs, + opts->group_map[SDAP_AT_GROUP_MODSTAMP].sys_name, + (const char*)el->values[0].data); + if (ret) { + goto fail; + } + state->timestamp = talloc_strdup(state, + (const char*)el->values[0].data); + if (!state->timestamp) { + ret = ENOMEM; + goto fail; + } + } + DEBUG(6, ("Storing info for group %s\n", state->name)); - subreq = sysdb_store_group_send(state, state->ev, state->handle, - state->dom, state->name, gid, members); + subreq = sysdb_store_group_send(state, state->ev, + state->handle, state->dom, + state->name, gid, members, + group_attrs); if (!subreq) { ret = ENOMEM; goto fail; @@ -1155,7 +1219,7 @@ static void sdap_save_group_done(struct tevent_req *subreq) ret = sysdb_store_group_recv(subreq); talloc_zfree(subreq); if (ret) { - DEBUG(2, ("Failed to save group %s\n", state->name)); + DEBUG(2, ("Failed to save group %s [%d]\n", state->name, ret)); tevent_req_error(req, ret); return; } @@ -1163,8 +1227,11 @@ static void sdap_save_group_done(struct tevent_req *subreq) tevent_req_done(req); } -int sdap_save_group_recv(struct tevent_req *req) +static int sdap_save_group_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, char **timestamp) { + struct sdap_save_group_state *state = tevent_req_data(req, + struct sdap_save_group_state); enum tevent_req_state tstate; uint64_t err; @@ -1173,6 +1240,8 @@ int sdap_save_group_recv(struct tevent_req *req) return err; } + *timestamp = talloc_steal(mem_ctx, state->timestamp); + return EOK; } @@ -1189,6 +1258,8 @@ struct sdap_get_users_state { struct sysdb_handle *handle; struct sdap_op *op; + + char *higher_timestamp; }; static void sdap_get_users_transaction(struct tevent_req *subreq); @@ -1218,6 +1289,7 @@ struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx, state->sh = sh; state->filter = filter; state->attrs = attrs; + state->higher_timestamp = NULL; subreq = sysdb_transaction_send(state, state->ev, sysdb); if (!subreq) return NULL; @@ -1344,23 +1416,41 @@ static void sdap_get_users_save_done(struct tevent_req *subreq) struct tevent_req); struct sdap_get_users_state *state = tevent_req_data(req, struct sdap_get_users_state); + char *timestamp; int ret; - ret = sdap_save_user_recv(subreq); + ret = sdap_save_user_recv(subreq, state, ×tamp); talloc_zfree(subreq); /* Do not fail completely on errors. * Just report the failure to save and go on */ if (ret) { DEBUG(2, ("Failed to store user. Ignoring.\n")); + timestamp = NULL; + } + + if (timestamp) { + if (state->higher_timestamp) { + if (strcmp(timestamp, state->higher_timestamp) > 0) { + talloc_zfree(state->higher_timestamp); + state->higher_timestamp = timestamp; + } else { + talloc_zfree(timestamp); + } + } else { + state->higher_timestamp = timestamp; + } } /* unlock the operation so that we can proceed with the next result */ sdap_unlock_next_reply(state->op); } -int sdap_get_users_recv(struct tevent_req *req) +int sdap_get_users_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, char **timestamp) { + struct sdap_get_users_state *state = tevent_req_data(req, + struct sdap_get_users_state); enum tevent_req_state tstate; uint64_t err; @@ -1369,6 +1459,10 @@ int sdap_get_users_recv(struct tevent_req *req) return EIO; } + if (timestamp) { + *timestamp = talloc_steal(mem_ctx, state->higher_timestamp); + } + return EOK; } @@ -1384,6 +1478,7 @@ struct sdap_get_groups_state { struct sysdb_handle *handle; struct sdap_op *op; + char *higher_timestamp; }; static void sdap_get_groups_transaction(struct tevent_req *subreq); @@ -1413,6 +1508,7 @@ struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx, state->sh = sh; state->filter = filter; state->attrs = attrs; + state->higher_timestamp = NULL; subreq = sysdb_transaction_send(state, state->ev, sysdb); if (!subreq) return NULL; @@ -1544,9 +1640,10 @@ static void sdap_get_groups_save_done(struct tevent_req *subreq) struct tevent_req); struct sdap_get_groups_state *state = tevent_req_data(req, struct sdap_get_groups_state); + char *timestamp; int ret; - ret = sdap_save_group_recv(subreq); + ret = sdap_save_group_recv(subreq, state, ×tamp); talloc_zfree(subreq); /* Do not fail completely on errors. @@ -1554,14 +1651,31 @@ static void sdap_get_groups_save_done(struct tevent_req *subreq) if (ret) { DEBUG(2, ("Failed to store group. Ignoring.\n")); + timestamp = NULL; + } + + if (timestamp) { + if (state->higher_timestamp) { + if (strcmp(timestamp, state->higher_timestamp) > 0) { + talloc_zfree(state->higher_timestamp); + state->higher_timestamp = timestamp; + } else { + talloc_zfree(timestamp); + } + } else { + state->higher_timestamp = timestamp; + } } /* unlock the operation so that we can proceed with the next result */ sdap_unlock_next_reply(state->op); } -int sdap_get_groups_recv(struct tevent_req *req) +int sdap_get_groups_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, char **timestamp) { + struct sdap_get_groups_state *state = tevent_req_data(req, + struct sdap_get_groups_state); enum tevent_req_state tstate; uint64_t err; @@ -1570,6 +1684,10 @@ int sdap_get_groups_recv(struct tevent_req *req) return EIO; } + if (timestamp) { + *timestamp = talloc_steal(mem_ctx, state->higher_timestamp); + } + return EOK; } @@ -1853,7 +1971,7 @@ static void sdap_get_initgr_save_done(struct tevent_req *subreq) struct sdap_get_initgr_state); int ret; - ret = sdap_save_group_recv(subreq); + ret = sdap_save_group_recv(subreq, NULL, NULL); talloc_zfree(subreq); /* Do not fail completely on errors. diff --git a/server/providers/ldap/sdap_async.h b/server/providers/ldap/sdap_async.h index 0cb0b9076..c9af750bb 100644 --- a/server/providers/ldap/sdap_async.h +++ b/server/providers/ldap/sdap_async.h @@ -40,7 +40,8 @@ struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx, struct sdap_handle *sh, const char **attrs, const char *wildcard); -int sdap_get_users_recv(struct tevent_req *req); +int sdap_get_users_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, char **timestamp); struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx, struct tevent_context *ev, @@ -50,7 +51,8 @@ struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx, struct sdap_handle *sh, const char **attrs, const char *wildcard); -int sdap_get_groups_recv(struct tevent_req *req); +int sdap_get_groups_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, char **timestamp); struct tevent_req *sdap_auth_send(TALLOC_CTX *memctx, struct tevent_context *ev, -- cgit