From e2925c2d7d10cbb51098402233784044168f1a77 Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Sat, 28 Jan 2012 13:32:32 -0500 Subject: LDAP: Add enumeration support for services --- src/providers/ldap/ldap_id.c | 2 + src/providers/ldap/ldap_id_enum.c | 60 +++++++++++++- src/providers/ldap/sdap.c | 5 ++ src/providers/ldap/sdap.h | 1 + src/providers/ldap/sdap_async.h | 11 +++ src/providers/ldap/sdap_async_services.c | 138 +++++++++++++++++++++++++++++++ src/providers/ldap/sdap_id_op.c | 1 + 7 files changed, 215 insertions(+), 3 deletions(-) diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c index db49e77d9..a433a8d53 100644 --- a/src/providers/ldap/ldap_id.c +++ b/src/providers/ldap/ldap_id.c @@ -731,11 +731,13 @@ static void sdap_check_online_done(struct tevent_req *req) if (!check_ctx->id_ctx->srv_opts) { srv_opts->max_user_value = 0; srv_opts->max_group_value = 0; + srv_opts->max_service_value = 0; } else if (strcmp(srv_opts->server_id, check_ctx->id_ctx->srv_opts->server_id) == 0 && srv_opts->supports_usn && check_ctx->id_ctx->srv_opts->last_usn > srv_opts->last_usn) { check_ctx->id_ctx->srv_opts->max_user_value = 0; check_ctx->id_ctx->srv_opts->max_group_value = 0; + check_ctx->id_ctx->srv_opts->max_service_value = 0; check_ctx->id_ctx->srv_opts->last_usn = srv_opts->last_usn; } diff --git a/src/providers/ldap/ldap_id_enum.c b/src/providers/ldap/ldap_id_enum.c index 7a8d0712e..3679a7d7f 100644 --- a/src/providers/ldap/ldap_id_enum.c +++ b/src/providers/ldap/ldap_id_enum.c @@ -199,6 +199,7 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx, struct sdap_id_op *op, bool purge); static void ldap_id_enum_groups_done(struct tevent_req *subreq); +static void ldap_id_enum_services_done(struct tevent_req *subreq); static void ldap_id_enum_cleanup_done(struct tevent_req *subreq); static struct tevent_req *ldap_id_enumerate_send(struct tevent_context *ev, @@ -362,7 +363,59 @@ static void ldap_id_enum_groups_done(struct tevent_req *subreq) } talloc_zfree(subreq); - ret = sdap_id_op_done(state->op, (int)err, &dp_error); + if (err != EOK) { + /* We call sdap_id_op_done only on error + * as the connection is reused by services enumeration */ + ret = sdap_id_op_done(state->op, (int)err, &dp_error); + if (dp_error == DP_ERR_OK && ret != EOK) { + /* retry */ + ret = ldap_id_enumerate_retry(req); + if (ret == EOK) { + return; + } + + dp_error = DP_ERR_FATAL; + } + + if (ret != EOK) { + if (dp_error == DP_ERR_OFFLINE) { + tevent_req_done(req); + } else { + DEBUG(9, ("Group enumeration failed with: (%d)[%s]\n", + ret, strerror(ret))); + tevent_req_error(req, ret); + } + + return; + } + } + + subreq = enum_services_send(state, state->ev, state->ctx, + state->op, state->purge); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, ldap_id_enum_services_done, req); +} + +static void ldap_id_enum_services_done(struct tevent_req *subreq) +{ + errno_t ret; + int dp_error = DP_ERR_FATAL; + 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); + + ret = enum_services_recv(subreq); + talloc_zfree(subreq); + if (ret == ENOENT) ret = EOK; + + /* All enumerations are complete, so conclude the + * id_op + */ + ret = sdap_id_op_done(state->op, ret, &dp_error); if (dp_error == DP_ERR_OK && ret != EOK) { /* retry */ ret = ldap_id_enumerate_retry(req); @@ -377,8 +430,9 @@ static void ldap_id_enum_groups_done(struct tevent_req *subreq) if (dp_error == DP_ERR_OFFLINE) { tevent_req_done(req); } else { - DEBUG(9, ("Group enumeration failed with: (%d)[%s]\n", - ret, strerror(ret))); + DEBUG(SSSDBG_MINOR_FAILURE, + ("Service enumeration failed with: (%d)[%s]\n", + ret, strerror(ret))); tevent_req_error(req, ret); } diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c index ba1c7911e..8a118150b 100644 --- a/src/providers/ldap/sdap.c +++ b/src/providers/ldap/sdap.c @@ -925,6 +925,11 @@ int sdap_get_server_opts_from_rootdse(TALLOC_CTX *memctx, talloc_strdup(opts->group_map, opts->gen_map[SDAP_AT_ENTRY_USN].name); } + if (!opts->service_map[SDAP_AT_SERVICE_USN].name) { + opts->service_map[SDAP_AT_SERVICE_USN].name = + talloc_strdup(opts->service_map, + opts->gen_map[SDAP_AT_ENTRY_USN].name); + } *srv_opts = so; return EOK; diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h index bbc59414a..7bf1805c1 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -359,6 +359,7 @@ struct sdap_server_opts { unsigned long last_usn; char *max_user_value; char *max_group_value; + char *max_service_value; }; struct sdap_id_ctx; diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h index abf16b0c6..8f8af47d5 100644 --- a/src/providers/ldap/sdap_async.h +++ b/src/providers/ldap/sdap_async.h @@ -28,6 +28,7 @@ #include #include "providers/dp_backend.h" #include "providers/ldap/sdap.h" +#include "providers/ldap/sdap_id_op.h" #include "providers/fail_over.h" struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx, @@ -228,4 +229,14 @@ sdap_get_services_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, char **usn_value); +struct tevent_req * +enum_services_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_id_ctx *id_ctx, + struct sdap_id_op *op, + bool purge); + +errno_t +enum_services_recv(struct tevent_req *req); + #endif /* _SDAP_ASYNC_H_ */ diff --git a/src/providers/ldap/sdap_async_services.c b/src/providers/ldap/sdap_async_services.c index 6fab4ace1..e4371f58e 100644 --- a/src/providers/ldap/sdap_async_services.c +++ b/src/providers/ldap/sdap_async_services.c @@ -483,3 +483,141 @@ sdap_get_services_recv(TALLOC_CTX *mem_ctx, return EOK; } + + +/* Enumeration routines */ + +struct enum_services_state { + struct tevent_context *ev; + struct sdap_id_ctx *id_ctx; + struct sdap_id_op *op; + struct sss_domain_info *domain; + struct sysdb_ctx *sysdb; + + char *filter; + const char **attrs; +}; + +static void +enum_services_op_done(struct tevent_req *subreq); + +struct tevent_req * +enum_services_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_id_ctx *id_ctx, + struct sdap_id_op *op, + bool purge) +{ + errno_t ret; + struct tevent_req *req; + struct tevent_req *subreq; + struct enum_services_state *state; + + req = tevent_req_create(memctx, &state, struct enum_services_state); + if (!req) return NULL; + + state->ev = ev; + state->id_ctx = id_ctx; + state->domain = id_ctx->be->domain; + state->sysdb = id_ctx->be->sysdb; + state->op = op; + + if (id_ctx->srv_opts && id_ctx->srv_opts->max_service_value && !purge) { + state->filter = talloc_asprintf( + state, + "(&(objectclass=%s)(%s=*)(%s=*)(%s=*)(%s>=%s)(!(%s=%s)))", + id_ctx->opts->service_map[SDAP_OC_SERVICE].name, + id_ctx->opts->service_map[SDAP_AT_SERVICE_NAME].name, + id_ctx->opts->service_map[SDAP_AT_SERVICE_PORT].name, + id_ctx->opts->service_map[SDAP_AT_SERVICE_PROTOCOL].name, + id_ctx->opts->service_map[SDAP_AT_SERVICE_USN].name, + id_ctx->srv_opts->max_service_value, + id_ctx->opts->service_map[SDAP_AT_SERVICE_USN].name, + id_ctx->srv_opts->max_service_value); + } else { + state->filter = talloc_asprintf( + state, + "(&(objectclass=%s)(%s=*)(%s=*)(%s=*))", + id_ctx->opts->service_map[SDAP_OC_SERVICE].name, + id_ctx->opts->service_map[SDAP_AT_SERVICE_NAME].name, + id_ctx->opts->service_map[SDAP_AT_SERVICE_PORT].name, + id_ctx->opts->service_map[SDAP_AT_SERVICE_PROTOCOL].name); + } + if (!state->filter) { + DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to build base filter\n")); + ret = ENOMEM; + goto fail; + } + + /* TODO: handle attrs_type */ + ret = build_attrs_from_map(state, id_ctx->opts->service_map, + SDAP_OPTS_SERVICES, &state->attrs); + if (ret != EOK) goto fail; + + subreq = sdap_get_services_send(state, state->ev, + state->domain, state->sysdb, + state->id_ctx->opts, + state->id_ctx->opts->service_search_bases, + sdap_id_op_handle(state->op), + state->attrs, state->filter, + dp_opt_get_int(state->id_ctx->opts->basic, + SDAP_SEARCH_TIMEOUT), + true); + if (!subreq) { + ret = ENOMEM; + goto fail; + } + tevent_req_set_callback(subreq, enum_services_op_done, req); + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void +enum_services_op_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct enum_services_state *state = + tevent_req_data(req, struct enum_services_state); + char *usn_value; + char *endptr = NULL; + unsigned usn_number; + int ret; + + ret = sdap_get_services_recv(state, subreq, &usn_value); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + if (usn_value) { + talloc_zfree(state->id_ctx->srv_opts->max_service_value); + state->id_ctx->srv_opts->max_service_value = + talloc_steal(state->id_ctx, usn_value); + + usn_number = strtoul(usn_value, &endptr, 10); + if ((endptr == NULL || (*endptr == '\0' && endptr != usn_value)) + && (usn_number > state->id_ctx->srv_opts->last_usn)) { + state->id_ctx->srv_opts->last_usn = usn_number; + } + } + + DEBUG(SSSDBG_FUNC_DATA, ("Services higher USN value: [%s]\n", + state->id_ctx->srv_opts->max_service_value)); + + tevent_req_done(req); +} + +errno_t +enum_services_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} diff --git a/src/providers/ldap/sdap_id_op.c b/src/providers/ldap/sdap_id_op.c index 5087cddc6..539f26f0c 100644 --- a/src/providers/ldap/sdap_id_op.c +++ b/src/providers/ldap/sdap_id_op.c @@ -541,6 +541,7 @@ static void sdap_id_op_connect_done(struct tevent_req *subreq) current_srv_opts->max_user_value = 0; current_srv_opts->max_group_value = 0; + current_srv_opts->max_service_value = 0; current_srv_opts->last_usn = srv_opts->last_usn; } } -- cgit