>From e115d033c87db19b9d1f955c3377a64b101078b3 Mon Sep 17 00:00:00 2001
From: Simo Sorce <ssorce@redhat.com>
Date: Thu, 25 Nov 2010 17:10:11 -0500
Subject: [PATCH 3/4] ldap: add checks to determine if USN features are available.

---
 src/providers/ipa/ipa_auth.c               |    2 +-
 src/providers/ldap/ldap_common.c           |   10 ++--
 src/providers/ldap/ldap_id.c               |    2 +-
 src/providers/ldap/sdap.c                  |   84 +++++++++++++++++++++++++++-
 src/providers/ldap/sdap.h                  |   17 ++++++
 src/providers/ldap/sdap_async.c            |    1 +
 src/providers/ldap/sdap_async.h            |    6 +-
 src/providers/ldap/sdap_async_connection.c |   28 ++++++---
 src/providers/ldap/sdap_id_op.c            |    9 ++-
 9 files changed, 134 insertions(+), 25 deletions(-)

diff --git a/src/providers/ipa/ipa_auth.c b/src/providers/ipa/ipa_auth.c
index 23095e53b39912d3adce9fb91f98e4fb8cabb076..020b63f3a54e9853143f8e4631eae3d22f134eaf 100644
--- a/src/providers/ipa/ipa_auth.c
+++ b/src/providers/ipa/ipa_auth.c
@@ -118,7 +118,7 @@ static void get_password_migration_flag_auth_done(struct tevent_req *subreq)
     char *search_base;
     const char **attrs;
 
-    ret = sdap_cli_connect_recv(subreq, state, &state->sh);
+    ret = sdap_cli_connect_recv(subreq, state, NULL, &state->sh, NULL);
     talloc_zfree(subreq);
     if (ret) {
         DEBUG(1, ("sdap_auth request failed.\n"));
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 4242a7e4519169664b69a953c0e2a95b045dc6b1..5e2572b871760fdd31d46b01fac01523a91edb22 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -81,17 +81,17 @@ struct dp_option default_basic_opts[] = {
 
 struct sdap_attr_map generic_attr_map[] = {
     { "ldap_entry_usn", NULL, SYSDB_USN, NULL },
-    { "ldap_rootdse_last_usn", NULL, SYSDB_USN, NULL }
+    { "ldap_rootdse_last_usn", NULL, SYSDB_HIGH_USN, NULL }
 };
 
 struct sdap_attr_map gen_ipa_attr_map[] = {
-    { "ldap_entry_usn", "entryUSN", SYSDB_USN, NULL },
-    { "ldap_rootdse_last_usn", "lastUSN", SYSDB_HIGH_USN, NULL }
+    { "ldap_entry_usn", SDAP_IPA_USN, SYSDB_USN, NULL },
+    { "ldap_rootdse_last_usn", SDAP_IPA_LAST_USN, SYSDB_HIGH_USN, NULL }
 };
 
 struct sdap_attr_map gen_ad_attr_map[] = {
-    { "ldap_entry_usn", "uSNChanged", SYSDB_USN, NULL },
-    { "ldap_rootdse_last_usn", "highestCommittedUSN", SYSDB_HIGH_USN, NULL }
+    { "ldap_entry_usn", SDAP_AD_USN, SYSDB_USN, NULL },
+    { "ldap_rootdse_last_usn", SDAP_AD_LAST_USN, SYSDB_HIGH_USN, NULL }
 };
 
 struct sdap_attr_map rfc2307_user_map[] = {
diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
index 9121a3e9feed639d330c65883344bfc7c4b95b1e..ce94731cbf27be298eb730bb228db8784b8eac54 100644
--- a/src/providers/ldap/ldap_id.c
+++ b/src/providers/ldap/ldap_id.c
@@ -673,7 +673,7 @@ static void sdap_check_online_done(struct tevent_req *req)
     int dp_err = DP_ERR_FATAL;
     bool can_retry;
 
-    ret = sdap_cli_connect_recv_ext(req, NULL, &can_retry, NULL);
+    ret = sdap_cli_connect_recv(req, NULL, &can_retry, NULL, NULL);
     talloc_zfree(req);
 
     if (ret != EOK) {
diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c
index 1a065cc804c2e8dec840c17d91acc658d6b12aa9..ffe625b3b92c0f4796880146e236f7a0d8df334e 100644
--- a/src/providers/ldap/sdap.c
+++ b/src/providers/ldap/sdap.c
@@ -495,7 +495,6 @@ errno_t sdap_set_config_options_with_rootdse(struct sysdb_attrs *rootdse,
                                         -1 };
     size_t c;
 
-
     for (c = 0; search_base_options[c] != -1; c++) {
         if (dp_opt_get_string(opts->basic, search_base_options[c]) == NULL) {
             if (naming_context == NULL) {
@@ -526,6 +525,89 @@ done:
     return ret;
 }
 
+int sdap_get_server_opts_from_rootdse(TALLOC_CTX *memctx,
+                                      const char *server,
+                                      struct sysdb_attrs *rootdse,
+                                      struct sdap_options *opts,
+                                      struct sdap_server_opts **srv_opts)
+{
+    struct sdap_server_opts *so;
+    struct {
+        const char *last_name;
+        const char *entry_name;
+    } usn_attrs[] = { { SDAP_IPA_LAST_USN, SDAP_IPA_USN },
+                      { SDAP_AD_LAST_USN, SDAP_AD_USN },
+                      { NULL, NULL } };
+    const char *last_usn_name;
+    const char *last_usn_value;
+    int ret;
+    int i;
+
+    so = talloc_zero(memctx, struct sdap_server_opts);
+    if (!so) {
+        return ENOMEM;
+    }
+    so->server_id = talloc_strdup(so, server);
+    if (!so->server_id) {
+        talloc_zfree(so);
+        return ENOMEM;
+    }
+
+    last_usn_name = opts->gen_map[SDAP_AT_LAST_USN].name;
+    if (last_usn_name) {
+        ret = sysdb_attrs_get_string(rootdse,
+                                      last_usn_name, &last_usn_value);
+        if (ret != EOK) {
+            switch (ret) {
+            case ENOENT:
+                DEBUG(1, ("%s configured but not found in rootdse!\n",
+                          opts->gen_map[SDAP_AT_LAST_USN].opt_name));
+                break;
+            case ERANGE:
+                DEBUG(1, ("Multiple values of %s found in rootdse!\n",
+                          opts->gen_map[SDAP_AT_LAST_USN].opt_name));
+                break;
+            default:
+                DEBUG(1, ("Unkown error (%d) checking rootdse!\n", ret));
+            }
+        } else {
+            const char *entry_usn_name;
+            entry_usn_name = opts->gen_map[SDAP_AT_ENTRY_USN].name;
+            if (!entry_usn_name) {
+                DEBUG(1, ("%s found in rootdse but %s is not set!\n",
+                          last_usn_name,
+                          opts->gen_map[SDAP_AT_ENTRY_USN].opt_name));
+            } else {
+                so->supports_usn = true;
+            }
+        }
+    } else {
+        /* no usn option configure, let's try to autodetect. */
+        for (i = 0; usn_attrs[i].last_name; i++) {
+            ret = sysdb_attrs_get_string(rootdse,
+                                         usn_attrs[i].last_name,
+                                         &last_usn_value);
+            if (ret == EOK) {
+                /* Fixate discovered configuration */
+                opts->gen_map[SDAP_AT_LAST_USN].name =
+                    talloc_strdup(opts->gen_map, usn_attrs[i].last_name);
+                opts->gen_map[SDAP_AT_ENTRY_USN].name =
+                    talloc_strdup(opts->gen_map, usn_attrs[i].entry_name);
+                so->supports_usn = true;
+                last_usn_name = usn_attrs[i].last_name;
+                break;
+            }
+        }
+    }
+
+    if (!last_usn_name) {
+        DEBUG(5, ("No known USN scheme is supported by this server\n!"));
+    }
+
+    *srv_opts = so;
+    return EOK;
+}
+
 int build_attrs_from_map(TALLOC_CTX *memctx,
                          struct sdap_attr_map *map,
                          size_t size, const char ***_attrs)
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index 5c4f4a548bbd9d177133252cc5e22fdf2371bf6c..68242f1a66607ec9284a21511d25eceaff42f8d1 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -130,6 +130,11 @@ struct sdap_ppolicy_data {
 #define SDAP_ROOTDSE_ATTR_NAMING_CONTEXTS "namingContexts"
 #define SDAP_ROOTDSE_ATTR_DEFAULT_NAMING_CONTEXT "defaultNamingContext"
 
+#define SDAP_IPA_USN "entryUSN"
+#define SDAP_IPA_LAST_USN "lastUSN"
+#define SDAP_AD_USN "uSNChanged"
+#define SDAP_AD_LAST_USN "highestCommittedUSN"
+
 enum sdap_result {
     SDAP_SUCCESS,
     SDAP_NOT_FOUND,
@@ -280,6 +285,13 @@ struct sdap_options {
     struct ldb_dn *groups_base;
 };
 
+struct sdap_server_opts {
+    char *server_id;
+    bool supports_usn;
+    char *max_user_value;
+    char *max_group_value;
+};
+
 int sdap_get_map(TALLOC_CTX *memctx,
                  struct confdb_ctx *cdb,
                  const char *conf_path,
@@ -328,4 +340,9 @@ int sdap_control_create(struct sdap_handle *sh, const char *oid, int iscritical,
 errno_t sdap_set_config_options_with_rootdse(struct sysdb_attrs *rootdse,
                                              struct sdap_handle *sh,
                                              struct sdap_options *opts);
+int sdap_get_server_opts_from_rootdse(TALLOC_CTX *memctx,
+                                      const char *server,
+                                      struct sysdb_attrs *rootdse,
+                                      struct sdap_options *opts,
+                                      struct sdap_server_opts **srv_opts);
 #endif /* _SDAP_H_ */
diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
index 6808ceaa9d10ae4cde0f8cbfe0a41a0143c58776..6929b29334e700eb1784995382a036633a876041 100644
--- a/src/providers/ldap/sdap_async.c
+++ b/src/providers/ldap/sdap_async.c
@@ -655,6 +655,7 @@ struct tevent_req *sdap_get_rootdse_send(TALLOC_CTX *memctx,
             "supportedLDAPVersion",
             "supportedSASLMechanisms",
             SDAP_ROOTDSE_ATTR_DEFAULT_NAMING_CONTEXT,
+            SDAP_IPA_LAST_USN, SDAP_AD_LAST_USN,
             NULL
     };
 
diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
index bf54bbd18972a01859b278018c47dd07f6c35dbc..10d0c3079e996f884b79078d136e5f100e8afaf9 100644
--- a/src/providers/ldap/sdap_async.h
+++ b/src/providers/ldap/sdap_async.h
@@ -129,11 +129,9 @@ struct tevent_req *sdap_cli_connect_send(TALLOC_CTX *memctx,
                                          bool skip_rootdse);
 int sdap_cli_connect_recv(struct tevent_req *req,
                           TALLOC_CTX *memctx,
-                          struct sdap_handle **gsh);
-int sdap_cli_connect_recv_ext(struct tevent_req *req,
-                          TALLOC_CTX *memctx,
                           bool *can_retry,
-                          struct sdap_handle **gsh);
+                          struct sdap_handle **gsh,
+                          struct sdap_server_opts **srv_opts);
 
 struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
                                          struct tevent_context *ev,
diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c
index f143a013eed35848a761b57a3f410a7ce1795ee4..2ad9639d6dc8ab333dcb3048cd0f976610bc0b5f 100644
--- a/src/providers/ldap/sdap_async_connection.c
+++ b/src/providers/ldap/sdap_async_connection.c
@@ -1063,6 +1063,8 @@ struct sdap_cli_connect_state {
     struct sdap_handle *sh;
 
     struct fo_server *srv;
+
+    struct sdap_server_opts *srv_opts;
 };
 
 static int sdap_cli_resolve_next(struct tevent_req *req);
@@ -1094,6 +1096,7 @@ struct tevent_req *sdap_cli_connect_send(TALLOC_CTX *memctx,
     state->service = service;
     state->be = be;
     state->srv = NULL;
+    state->srv_opts = NULL;
     state->be = be;
     state->use_rootdse = !skip_rootdse;
 
@@ -1286,6 +1289,15 @@ static void sdap_cli_rootdse_done(struct tevent_req *subreq)
         return;
     }
 
+    ret = sdap_get_server_opts_from_rootdse(state,
+                                            state->service->uri, rootdse,
+                                            state->opts, &state->srv_opts);
+    if (ret) {
+        DEBUG(1, ("sdap_get_server_opts_from_rootdse failed.\n"));
+        tevent_req_error(req, ret);
+        return;
+    }
+
     sasl_mech = dp_opt_get_string(state->opts->basic, SDAP_SASL_MECH);
 
     if (sasl_mech && state->use_rootdse) {
@@ -1416,15 +1428,9 @@ static void sdap_cli_auth_done(struct tevent_req *subreq)
 
 int sdap_cli_connect_recv(struct tevent_req *req,
                           TALLOC_CTX *memctx,
-                          struct sdap_handle **gsh)
-{
-    return sdap_cli_connect_recv_ext(req, memctx, NULL, gsh);
-}
-
-int sdap_cli_connect_recv_ext(struct tevent_req *req,
-                              TALLOC_CTX *memctx,
-                              bool *can_retry,
-                              struct sdap_handle **gsh)
+                          bool *can_retry,
+                          struct sdap_handle **gsh,
+                          struct sdap_server_opts **srv_opts)
 {
     struct sdap_cli_connect_state *state = tevent_req_data(req,
                                              struct sdap_cli_connect_state);
@@ -1464,6 +1470,10 @@ int sdap_cli_connect_recv_ext(struct tevent_req *req,
         talloc_zfree(state->sh);
     }
 
+    if (srv_opts) {
+        *srv_opts = talloc_steal(memctx, state->srv_opts);
+    }
+
     return EOK;
 }
 
diff --git a/src/providers/ldap/sdap_id_op.c b/src/providers/ldap/sdap_id_op.c
index a79c64b9c99618dd66db5776ee0101b70d3b5078..c4194efbcabcd7b70eb9dad3114badd66352a717 100644
--- a/src/providers/ldap/sdap_id_op.c
+++ b/src/providers/ldap/sdap_id_op.c
@@ -494,15 +494,16 @@ done:
 /* Subrequest callback for connection completion */
 static void sdap_id_op_connect_done(struct tevent_req *subreq)
 {
-    struct sdap_id_conn_data *conn_data = tevent_req_callback_data(subreq,
-                                                                   struct sdap_id_conn_data);
+    struct sdap_id_conn_data *conn_data =
+                tevent_req_callback_data(subreq, struct sdap_id_conn_data);
     struct sdap_id_conn_cache *conn_cache = conn_data->conn_cache;
+    struct sdap_server_opts *srv_opts;
     bool can_retry = false;
     bool is_offline = false;
     int ret;
 
-    ret = sdap_cli_connect_recv_ext(subreq, conn_data, &can_retry,
-                                    &conn_data->sh);
+    ret = sdap_cli_connect_recv(subreq, conn_data, &can_retry,
+                                &conn_data->sh, &srv_opts);
     conn_data->connect_req = NULL;
     talloc_zfree(subreq);
 
-- 
1.7.3.2

