From 906a2a4445296cb26944e52d976e1342df42de36 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Sat, 28 Jan 2012 13:32:32 -0500
Subject: [PATCH 4/6] 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 db49e77d98da8ab5ca3acf5c7f1af48198f6fb7d..a433a8d53a8b846605d6b5f3e2606a60378f8dea 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 7a8d0712ec8a93a3b5d97bfb12b133a65d767996..3679a7d7f08ab01b48805edca3df07b6eca7367b 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 ba1c7911e0fb3a72de93e93e209e14f5996d7a7f..8a118150b383380b5a2ac1389712adb99bb8ef0c 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 bbc59414a724df49068dd611dca3c965f3ee8386..7bf1805c1798752e87d30e8173ea1b7c4944078b 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 abf16b0c6c30bb5cbcb0d338ab3bc842cdd2337b..8f8af47d5b17792325eb504d0b25f3658bb766ba 100644
--- a/src/providers/ldap/sdap_async.h
+++ b/src/providers/ldap/sdap_async.h
@@ -28,6 +28,7 @@
 #include <tevent.h>
 #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 6fab4ace13dd9901d9e75229b74838a5baa5a4d9..e4371f58e3ed67c3d3c0de58799687efb28e0167 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 5087cddc683ad813c4f6065aaafb1afb2b544787..539f26f0c839be16126a563cc0f3dd4116f7bacb 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;
             }
         }
-- 
1.7.7.6

