>From 535fc642412b385d0649100b3f9267089297a082 Mon Sep 17 00:00:00 2001
From: Simo Sorce <ssorce@redhat.com>
Date: Fri, 26 Nov 2010 13:59:32 -0500
Subject: [PATCH 4/4] ldap: Use USN entries if available.

Otherwise fallback to the default modifyTimestamp indicator
---
 src/config/etc/sssd.api.d/sssd-ipa.conf  |    2 +
 src/config/etc/sssd.api.d/sssd-ldap.conf |    2 +
 src/providers/ipa/ipa_common.c           |    4 +-
 src/providers/ldap/ldap_common.c         |    8 ++-
 src/providers/ldap/ldap_common.h         |    3 +-
 src/providers/ldap/ldap_id_enum.c        |   52 ++++++------
 src/providers/ldap/sdap.c                |   58 +++++++++++-
 src/providers/ldap/sdap.h                |    7 ++
 src/providers/ldap/sdap_async_accounts.c |  144 +++++++++++++++++++-----------
 src/providers/ldap/sdap_id_op.c          |    3 +-
 10 files changed, 194 insertions(+), 89 deletions(-)

diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf b/src/config/etc/sssd.api.d/sssd-ipa.conf
index 04b6632d53e926986a6426e59f85dadec7aa2938..7c1c35a2a747a1b18c9723b4834e2752cdc7b655 100644
--- a/src/config/etc/sssd.api.d/sssd-ipa.conf
+++ b/src/config/etc/sssd.api.d/sssd-ipa.conf
@@ -52,6 +52,7 @@ ldap_user_principal = str, None, false
 ldap_user_fullname = str, None, false
 ldap_user_member_of = str, None, false
 ldap_user_modify_timestamp = str, None, false
+ldap_user_entry_usn = str, None, false
 ldap_user_shadow_last_change = str, None, false
 ldap_user_shadow_min = str, None, false
 ldap_user_shadow_max = str, None, false
@@ -71,6 +72,7 @@ ldap_group_gid_number = str, None, false
 ldap_group_member = str, None, false
 ldap_group_uuid = str, None, false
 ldap_group_modify_timestamp = str, None, false
+ldap_group_entry_usn = str, None, false
 ldap_force_upper_case_realm = bool, None, false
 ldap_group_nesting_level = int, None, false
 ldap_netgroup_search_base = str, None, false
diff --git a/src/config/etc/sssd.api.d/sssd-ldap.conf b/src/config/etc/sssd.api.d/sssd-ldap.conf
index 2d44fb4ba75aa9f28d8bbb3f1330e4f24037f485..66f55275adff5757859a4b8835b0f98c3e18d841 100644
--- a/src/config/etc/sssd.api.d/sssd-ldap.conf
+++ b/src/config/etc/sssd.api.d/sssd-ldap.conf
@@ -45,6 +45,7 @@ ldap_user_principal = str, None, false
 ldap_user_fullname = str, None, false
 ldap_user_member_of = str, None, false
 ldap_user_modify_timestamp = str, None, false
+ldap_user_entry_usn = str, None, false
 ldap_user_shadow_last_change = str, None, false
 ldap_user_shadow_min = str, None, false
 ldap_user_shadow_max = str, None, false
@@ -64,6 +65,7 @@ ldap_group_gid_number = str, None, false
 ldap_group_member = str, None, false
 ldap_group_uuid = str, None, false
 ldap_group_modify_timestamp = str, None, false
+ldap_group_entry_usn = str, None, false
 ldap_group_nesting_level = int, None, false
 ldap_force_upper_case_realm = bool, None, false
 ldap_netgroup_search_base = str, None, false
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 8da83ee0852a94d9c9dad718ebfa7ad2dd0ee9a9..c07a9f11ec29e8a568d2802dc044e81c3342ae5f 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -103,6 +103,7 @@ struct sdap_attr_map ipa_user_map[] = {
     { "ldap_user_member_of", "memberOf", SYSDB_MEMBEROF, NULL },
     { "ldap_user_uuid", "nsUniqueId", SYSDB_UUID, NULL },
     { "ldap_user_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL },
+    { "ldap_user_entry_usn", NULL, SYSDB_USN, NULL },
     { "ldap_user_shadow_last_change", "shadowLastChange", SYSDB_SHADOWPW_LASTCHANGE, NULL },
     { "ldap_user_shadow_min", "shadowMin", SYSDB_SHADOWPW_MIN, NULL },
     { "ldap_user_shadow_max", "shadowMax", SYSDB_SHADOWPW_MAX, NULL },
@@ -122,7 +123,8 @@ struct sdap_attr_map ipa_group_map[] = {
     { "ldap_group_gid_number", "gidNumber", SYSDB_GIDNUM, NULL },
     { "ldap_group_member", "member", SYSDB_MEMBER, NULL },
     { "ldap_group_uuid", "nsUniqueId", SYSDB_UUID, NULL },
-    { "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL }
+    { "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL },
+    { "ldap_group_entry_usn", NULL, SYSDB_USN, NULL }
 };
 
 struct sdap_attr_map ipa_netgroup_map[] = {
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 5e2572b871760fdd31d46b01fac01523a91edb22..4d9fabc4e2a8e466c5fb86e63ade4b04eeebcbb0 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -108,6 +108,7 @@ struct sdap_attr_map rfc2307_user_map[] = {
     { "ldap_user_member_of", NULL, SYSDB_MEMBEROF, NULL },
     { "ldap_user_uuid", NULL, SYSDB_UUID, NULL },
     { "ldap_user_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL },
+    { "ldap_user_entry_usn", NULL, SYSDB_USN, NULL },
     { "ldap_user_shadow_last_change", "shadowLastChange", SYSDB_SHADOWPW_LASTCHANGE, NULL },
     { "ldap_user_shadow_min", "shadowMin", SYSDB_SHADOWPW_MIN, NULL },
     { "ldap_user_shadow_max", "shadowMax", SYSDB_SHADOWPW_MAX, NULL },
@@ -127,7 +128,8 @@ struct sdap_attr_map rfc2307_group_map[] = {
     { "ldap_group_gid_number", "gidNumber", SYSDB_GIDNUM, NULL },
     { "ldap_group_member", "memberuid", SYSDB_MEMBER, NULL },
     { "ldap_group_uuid", NULL, SYSDB_UUID, NULL },
-    { "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL }
+    { "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL },
+    { "ldap_group_entry_usn", NULL, SYSDB_USN, NULL }
 };
 
 struct sdap_attr_map rfc2307bis_user_map[] = {
@@ -145,6 +147,7 @@ struct sdap_attr_map rfc2307bis_user_map[] = {
     /* FIXME: this is 389ds specific */
     { "ldap_user_uuid", "nsUniqueId", SYSDB_UUID, NULL },
     { "ldap_user_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL },
+    { "ldap_user_entry_usn", NULL, SYSDB_USN, NULL },
     { "ldap_user_shadow_last_change", "shadowLastChange", SYSDB_SHADOWPW_LASTCHANGE, NULL },
     { "ldap_user_shadow_min", "shadowMin", SYSDB_SHADOWPW_MIN, NULL },
     { "ldap_user_shadow_max", "shadowMax", SYSDB_SHADOWPW_MAX, NULL },
@@ -165,7 +168,8 @@ struct sdap_attr_map rfc2307bis_group_map[] = {
     { "ldap_group_member", "member", SYSDB_MEMBER, NULL },
     /* FIXME: this is 389ds specific */
     { "ldap_group_uuid", "nsUniqueId", SYSDB_UUID, NULL },
-    { "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL }
+    { "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL },
+    { "ldap_group_entry_usn", NULL, SYSDB_USN, NULL }
 };
 
 struct sdap_attr_map netgroup_map[] = {
diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index f6b5c11827f14fe3b7b6e7eb2002c954f5c4e398..8f25e423289477b1bd86be959dafc2f28db22a3f 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -54,8 +54,7 @@ struct sdap_id_ctx {
     /* cleanup loop timer */
     struct timeval last_purge;
 
-    char *max_user_timestamp;
-    char *max_group_timestamp;
+    struct sdap_server_opts *srv_opts;
 };
 
 struct sdap_auth_ctx {
diff --git a/src/providers/ldap/ldap_id_enum.c b/src/providers/ldap/ldap_id_enum.c
index d60519213fb113ebbe15720e29e95b3dc388f3ae..9391614375a0be38deb2043e89ed83fb45bf93e7 100644
--- a/src/providers/ldap/ldap_id_enum.c
+++ b/src/providers/ldap/ldap_id_enum.c
@@ -417,6 +417,7 @@ static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
 {
     struct tevent_req *req, *subreq;
     struct enum_users_state *state;
+    const char *attr_name;
     int ret;
 
     req = tevent_req_create(memctx, &state, struct enum_users_state);
@@ -426,16 +427,15 @@ static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
     state->ctx = ctx;
     state->op = op;
 
-    if (ctx->max_user_timestamp && !purge) {
-
+    if (ctx->srv_opts && ctx->srv_opts->max_user_value && !purge) {
         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);
+                               ctx->opts->user_map[SDAP_AT_USER_USN].name,
+                               ctx->srv_opts->max_user_value,
+                               ctx->opts->user_map[SDAP_AT_USER_USN].name,
+                               ctx->srv_opts->max_user_value);
     } else {
         state->filter = talloc_asprintf(state,
                                "(&(%s=*)(objectclass=%s))",
@@ -479,23 +479,23 @@ static void enum_users_op_done(struct tevent_req *subreq)
                                                       struct tevent_req);
     struct enum_users_state *state = tevent_req_data(req,
                                                      struct enum_users_state);
-    char *timestamp;
+    char *usn_value;
     int ret;
 
-    ret = sdap_get_users_recv(subreq, state, &timestamp);
+    ret = sdap_get_users_recv(subreq, state, &usn_value);
     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);
+    if (usn_value) {
+        talloc_zfree(state->ctx->srv_opts->max_user_value);
+        state->ctx->srv_opts->max_user_value = talloc_steal(state->ctx, usn_value);
     }
 
-    DEBUG(4, ("Users higher timestamp: [%s]\n",
-              state->ctx->max_user_timestamp));
+    DEBUG(4, ("Users higher USN value: [%s]\n",
+              state->ctx->srv_opts->max_user_value));
 
     tevent_req_done(req);
 }
@@ -530,16 +530,15 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
     state->ctx = ctx;
     state->op = op;
 
-    if (ctx->max_group_timestamp && !purge) {
-
+    if (ctx->srv_opts && ctx->srv_opts->max_group_value && !purge) {
         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);
+                              ctx->opts->group_map[SDAP_AT_GROUP_USN].name,
+                              ctx->srv_opts->max_group_value,
+                              ctx->opts->group_map[SDAP_AT_GROUP_USN].name,
+                              ctx->srv_opts->max_group_value);
     } else {
         state->filter = talloc_asprintf(state,
                               "(&(%s=*)(objectclass=%s))",
@@ -582,23 +581,24 @@ static void enum_groups_op_done(struct tevent_req *subreq)
                                                       struct tevent_req);
     struct enum_groups_state *state = tevent_req_data(req,
                                                  struct enum_groups_state);
-    char *timestamp;
+    char *usn_value;
     int ret;
 
-    ret = sdap_get_groups_recv(subreq, state, &timestamp);
+    ret = sdap_get_groups_recv(subreq, state, &usn_value);
     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);
+    if (usn_value) {
+        talloc_zfree(state->ctx->srv_opts->max_group_value);
+        state->ctx->srv_opts->max_group_value =
+                                        talloc_steal(state->ctx, usn_value);
     }
 
-    DEBUG(4, ("Groups higher timestamp: [%s]\n",
-              state->ctx->max_group_timestamp));
+    DEBUG(4, ("Groups higher USN value: [%s]\n",
+              state->ctx->srv_opts->max_group_value));
 
     tevent_req_done(req);
 }
diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c
index ffe625b3b92c0f4796880146e236f7a0d8df334e..1735ac5200608131f949e54a79a2a876e446d019 100644
--- a/src/providers/ldap/sdap.c
+++ b/src/providers/ldap/sdap.c
@@ -22,6 +22,7 @@
 #define LDAP_DEPRECATED 1
 #include "util/util.h"
 #include "confdb/confdb.h"
+#include "providers/ldap/ldap_common.h"
 #include "providers/ldap/sdap.h"
 
 /* =Retrieve-Options====================================================== */
@@ -540,6 +541,7 @@ int sdap_get_server_opts_from_rootdse(TALLOC_CTX *memctx,
                       { NULL, NULL } };
     const char *last_usn_name;
     const char *last_usn_value;
+    const char *entry_usn_name;
     int ret;
     int i;
 
@@ -554,6 +556,7 @@ int sdap_get_server_opts_from_rootdse(TALLOC_CTX *memctx,
     }
 
     last_usn_name = opts->gen_map[SDAP_AT_LAST_USN].name;
+    entry_usn_name = opts->gen_map[SDAP_AT_ENTRY_USN].name;
     if (last_usn_name) {
         ret = sysdb_attrs_get_string(rootdse,
                                       last_usn_name, &last_usn_value);
@@ -571,8 +574,6 @@ int sdap_get_server_opts_from_rootdse(TALLOC_CTX *memctx,
                 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,
@@ -601,13 +602,53 @@ int sdap_get_server_opts_from_rootdse(TALLOC_CTX *memctx,
     }
 
     if (!last_usn_name) {
-        DEBUG(5, ("No known USN scheme is supported by this server\n!"));
+        DEBUG(5, ("No known USN scheme is supported by this server!\n"));
+        if (!entry_usn_name) {
+            DEBUG(5, ("Will use modification timestamp as usn!\n"));
+            opts->gen_map[SDAP_AT_ENTRY_USN].name =
+                talloc_strdup(opts->gen_map, "modifyTimestamp");
+        }
+    }
+
+    if (!opts->user_map[SDAP_AT_USER_USN].name) {
+        opts->user_map[SDAP_AT_USER_USN].name =
+                    talloc_strdup(opts->user_map,
+                                  opts->gen_map[SDAP_AT_ENTRY_USN].name);
+    }
+    if (!opts->group_map[SDAP_AT_GROUP_USN].name) {
+        opts->group_map[SDAP_AT_GROUP_USN].name =
+                    talloc_strdup(opts->group_map,
+                                  opts->gen_map[SDAP_AT_ENTRY_USN].name);
     }
 
     *srv_opts = so;
     return EOK;
 }
 
+void sdap_steal_server_opts(struct sdap_id_ctx *id_ctx,
+                            struct sdap_server_opts **srv_opts)
+{
+    if (!id_ctx || !srv_opts || !*srv_opts) {
+        return;
+    }
+
+    if (!id_ctx->srv_opts) {
+        id_ctx->srv_opts = talloc_move(id_ctx, srv_opts);
+        return;
+    }
+
+    /* discard if same as previous so we do not reset max usn values
+     * unnecessarily */
+    if (strcmp(id_ctx->srv_opts->server_id, (*srv_opts)->server_id) == 0) {
+        talloc_zfree(*srv_opts);
+        return;
+    }
+
+    talloc_zfree(id_ctx->srv_opts);
+    id_ctx->srv_opts = talloc_move(id_ctx, srv_opts);
+}
+
+
 int build_attrs_from_map(TALLOC_CTX *memctx,
                          struct sdap_attr_map *map,
                          size_t size, const char ***_attrs)
@@ -636,6 +677,17 @@ int build_attrs_from_map(TALLOC_CTX *memctx,
     return EOK;
 }
 
+int append_attrs_to_array(const char **attrs, size_t size, const char *attr)
+{
+    attrs = talloc_realloc(NULL, attrs, const char *, size + 2);
+    if (!attrs) return ENOMEM;
+
+    attrs[size] = attr;
+    attrs[size + 1] = NULL;
+
+    return EOK;
+}
+
 int sdap_control_create(struct sdap_handle *sh, const char *oid, int iscritical,
                         struct berval *value, int dupval, LDAPControl **ctrlp)
 {
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index 68242f1a66607ec9284a21511d25eceaff42f8d1..83bfd21b43ff57607369da68d4be7dd9253e90e7 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -218,6 +218,7 @@ enum sdap_user_attrs {
     SDAP_AT_USER_MEMBEROF,
     SDAP_AT_USER_UUID,
     SDAP_AT_USER_MODSTAMP,
+    SDAP_AT_USER_USN,
     SDAP_AT_SP_LSTCHG,
     SDAP_AT_SP_MIN,
     SDAP_AT_SP_MAX,
@@ -244,6 +245,7 @@ enum sdap_group_attrs {
     SDAP_AT_GROUP_MEMBER,
     SDAP_AT_GROUP_UUID,
     SDAP_AT_GROUP_MODSTAMP,
+    SDAP_AT_GROUP_USN,
 
     SDAP_OPTS_GROUP /* attrs counter */
 };
@@ -292,6 +294,8 @@ struct sdap_server_opts {
     char *max_group_value;
 };
 
+struct sdap_id_ctx;
+
 int sdap_get_map(TALLOC_CTX *memctx,
                  struct confdb_ctx *cdb,
                  const char *conf_path,
@@ -333,6 +337,7 @@ bool sdap_check_sup_list(struct sup_list *l, const char *val);
 int build_attrs_from_map(TALLOC_CTX *memctx,
                          struct sdap_attr_map *map,
                          size_t size, const char ***_attrs);
+int append_attrs_to_array(const char **attrs, size_t size, const char *attr);
 
 int sdap_control_create(struct sdap_handle *sh, const char *oid, int iscritical,
                         struct berval *value, int dupval, LDAPControl **ctrlp);
@@ -345,4 +350,6 @@ int sdap_get_server_opts_from_rootdse(TALLOC_CTX *memctx,
                                       struct sysdb_attrs *rootdse,
                                       struct sdap_options *opts,
                                       struct sdap_server_opts **srv_opts);
+void sdap_steal_server_opts(struct sdap_id_ctx *id_ctx,
+                            struct sdap_server_opts **srv_opts);
 #endif /* _SDAP_H_ */
diff --git a/src/providers/ldap/sdap_async_accounts.c b/src/providers/ldap/sdap_async_accounts.c
index 9856e75488b977cd2951d9871bf0e21cc816a1bd..004c69aff32ff7640fbc62841607fa713e5b1a61 100644
--- a/src/providers/ldap/sdap_async_accounts.c
+++ b/src/providers/ldap/sdap_async_accounts.c
@@ -34,7 +34,7 @@ static int sdap_save_user(TALLOC_CTX *memctx,
                           struct sss_domain_info *dom,
                           struct sysdb_attrs *attrs,
                           bool is_initgr,
-                          char **_timestamp)
+                          char **_usn_value)
 {
     struct ldb_message_element *el;
     int ret;
@@ -50,7 +50,7 @@ static int sdap_save_user(TALLOC_CTX *memctx,
     int i;
     char *val = NULL;
     int cache_timeout;
-    char *timestamp = NULL;
+    char *usn_value = NULL;
 
     DEBUG(9, ("Save user\n"));
 
@@ -181,8 +181,25 @@ static int sdap_save_user(TALLOC_CTX *memctx,
         if (ret) {
             goto fail;
         }
-        timestamp = talloc_strdup(memctx, (const char*)el->values[0].data);
-        if (!timestamp) {
+    }
+
+    ret = sysdb_attrs_get_el(attrs,
+                      opts->user_map[SDAP_AT_USER_USN].sys_name, &el);
+    if (ret) {
+        goto fail;
+    }
+    if (el->num_values == 0) {
+        DEBUG(7, ("Original USN value is not available for [%s].\n",
+                  name));
+    } else {
+        ret = sysdb_attrs_add_string(user_attrs,
+                          opts->user_map[SDAP_AT_USER_USN].sys_name,
+                          (const char*)el->values[0].data);
+        if (ret) {
+            goto fail;
+        }
+        usn_value = talloc_strdup(memctx, (const char*)el->values[0].data);
+        if (!usn_value) {
             ret = ENOMEM;
             goto fail;
         }
@@ -252,8 +269,8 @@ static int sdap_save_user(TALLOC_CTX *memctx,
                            user_attrs, cache_timeout);
     if (ret) goto fail;
 
-    if (_timestamp) {
-        *_timestamp = timestamp;
+    if (_usn_value) {
+        *_usn_value = usn_value;
     }
 
     return EOK;
@@ -272,11 +289,11 @@ static int sdap_save_users(TALLOC_CTX *memctx,
                            struct sdap_options *opts,
                            struct sysdb_attrs **users,
                            int num_users,
-                           char **_timestamp)
+                           char **_usn_value)
 {
     TALLOC_CTX *tmpctx;
-    char *higher_timestamp = NULL;
-    char *timestamp;
+    char *higher_usn = NULL;
+    char *usn_value;
     int ret;
     int i;
 
@@ -296,10 +313,10 @@ static int sdap_save_users(TALLOC_CTX *memctx,
     }
 
     for (i = 0; i < num_users; i++) {
-        timestamp = NULL;
+        usn_value = NULL;
 
         ret = sdap_save_user(tmpctx, sysdb, opts, dom,
-                             users[i], false, &timestamp);
+                             users[i], false, &usn_value);
 
         /* Do not fail completely on errors.
          * Just report the failure to save and go on */
@@ -309,16 +326,17 @@ static int sdap_save_users(TALLOC_CTX *memctx,
             DEBUG(9, ("User %d processed!\n", i));
         }
 
-        if (timestamp) {
-            if (higher_timestamp) {
-                if (strcmp(timestamp, higher_timestamp) > 0) {
-                    talloc_zfree(higher_timestamp);
-                    higher_timestamp = timestamp;
+        if (usn_value) {
+            if (higher_usn) {
+                if ((strlen(usn_value) > strlen(higher_usn)) ||
+                    (strcmp(usn_value, higher_usn) > 0)) {
+                    talloc_zfree(higher_usn);
+                    higher_usn = usn_value;
                 } else {
-                    talloc_zfree(timestamp);
+                    talloc_zfree(usn_value);
                 }
             } else {
-                higher_timestamp = timestamp;
+                higher_usn = usn_value;
             }
         }
     }
@@ -329,8 +347,8 @@ static int sdap_save_users(TALLOC_CTX *memctx,
         goto done;
     }
 
-    if (_timestamp) {
-        *_timestamp = talloc_steal(memctx, higher_timestamp);
+    if (_usn_value) {
+        *_usn_value = talloc_steal(memctx, higher_usn);
     }
 
 done:
@@ -350,7 +368,7 @@ struct sdap_get_users_state {
     const char **attrs;
     const char *filter;
 
-    char *higher_timestamp;
+    char *higher_usn;
     struct sysdb_attrs **users;
     size_t count;
 };
@@ -379,7 +397,7 @@ struct tevent_req *sdap_get_users_send(TALLOC_CTX *memctx,
     state->sysdb = sysdb;
     state->filter = filter;
     state->attrs = attrs;
-    state->higher_timestamp = NULL;
+    state->higher_usn = NULL;
     state->users =  NULL;
     state->count = 0;
 
@@ -424,7 +442,7 @@ static void sdap_get_users_process(struct tevent_req *subreq)
     ret = sdap_save_users(state, state->sysdb,
                           state->dom, state->opts,
                           state->users, state->count,
-                          &state->higher_timestamp);
+                          &state->higher_usn);
     if (ret) {
         DEBUG(2, ("Failed to store users.\n"));
         tevent_req_error(req, ret);
@@ -437,15 +455,15 @@ static void sdap_get_users_process(struct tevent_req *subreq)
 }
 
 int sdap_get_users_recv(struct tevent_req *req,
-                        TALLOC_CTX *mem_ctx, char **timestamp)
+                        TALLOC_CTX *mem_ctx, char **usn_value)
 {
     struct sdap_get_users_state *state = tevent_req_data(req,
                                             struct sdap_get_users_state);
 
     TEVENT_REQ_RETURN_ON_ERROR(req);
 
-    if (timestamp) {
-        *timestamp = talloc_steal(mem_ctx, state->higher_timestamp);
+    if (usn_value) {
+        *usn_value = talloc_steal(mem_ctx, state->higher_usn);
     }
 
     return EOK;
@@ -601,14 +619,14 @@ static int sdap_save_group(TALLOC_CTX *memctx,
                            struct sysdb_attrs *attrs,
                            bool store_members,
                            bool populate_members,
-                           char **_timestamp)
+                           char **_usn_value)
 {
     struct ldb_message_element *el;
     struct sysdb_attrs *group_attrs;
     const char *name = NULL;
     gid_t gid;
     int ret;
-    char *timestamp = NULL;
+    char *usn_value = NULL;
 
     ret = sysdb_attrs_get_el(attrs,
                           opts->group_map[SDAP_AT_GROUP_NAME].sys_name, &el);
@@ -674,8 +692,25 @@ static int sdap_save_group(TALLOC_CTX *memctx,
         if (ret) {
             goto fail;
         }
-        timestamp = talloc_strdup(memctx, (const char*)el->values[0].data);
-        if (!timestamp) {
+    }
+
+    ret = sysdb_attrs_get_el(attrs,
+                      opts->group_map[SDAP_AT_GROUP_USN].sys_name, &el);
+    if (ret) {
+        goto fail;
+    }
+    if (el->num_values == 0) {
+        DEBUG(7, ("Original USN value is not available for [%s].\n",
+                  name));
+    } else {
+        ret = sysdb_attrs_add_string(group_attrs,
+                          opts->group_map[SDAP_AT_GROUP_USN].sys_name,
+                          (const char*)el->values[0].data);
+        if (ret) {
+            goto fail;
+        }
+        usn_value = talloc_strdup(memctx, (const char*)el->values[0].data);
+        if (!usn_value) {
             ret = ENOMEM;
             goto fail;
         }
@@ -721,8 +756,8 @@ static int sdap_save_group(TALLOC_CTX *memctx,
                                            SDAP_ENTRY_CACHE_TIMEOUT));
     if (ret) goto fail;
 
-    if (_timestamp) {
-        *_timestamp = timestamp;
+    if (_usn_value) {
+        *_usn_value = usn_value;
     }
 
     return EOK;
@@ -805,11 +840,11 @@ static int sdap_save_groups(TALLOC_CTX *memctx,
                             struct sysdb_attrs **groups,
                             int num_groups,
                             bool populate_members,
-                            char **_timestamp)
+                            char **_usn_value)
 {
     TALLOC_CTX *tmpctx;
-    char *higher_timestamp = NULL;
-    char *timestamp;
+    char *higher_usn = NULL;
+    char *usn_value;
     bool twopass;
     int ret;
     int i;
@@ -840,12 +875,12 @@ static int sdap_save_groups(TALLOC_CTX *memctx,
     }
 
     for (i = 0; i < num_groups; i++) {
-        timestamp = NULL;
+        usn_value = NULL;
 
         /* if 2 pass savemembers = false */
         ret = sdap_save_group(tmpctx, sysdb,
                               opts, dom, groups[i],
-                              (!twopass), populate_members, &timestamp);
+                              (!twopass), populate_members, &usn_value);
 
         /* Do not fail completely on errors.
          * Just report the failure to save and go on */
@@ -855,16 +890,17 @@ static int sdap_save_groups(TALLOC_CTX *memctx,
             DEBUG(9, ("Group %d processed!\n", i));
         }
 
-        if (timestamp) {
-            if (higher_timestamp) {
-                if (strcmp(timestamp, higher_timestamp) > 0) {
-                    talloc_zfree(higher_timestamp);
-                    higher_timestamp = timestamp;
+        if (usn_value) {
+            if (higher_usn) {
+                if ((strlen(usn_value) > strlen(higher_usn)) ||
+                    (strcmp(usn_value, higher_usn) > 0)) {
+                    talloc_zfree(higher_usn);
+                    higher_usn = usn_value;
                 } else {
-                    talloc_zfree(timestamp);
+                    talloc_zfree(usn_value);
                 }
             } else {
-                higher_timestamp = timestamp;
+                higher_usn = usn_value;
             }
         }
     }
@@ -890,8 +926,8 @@ static int sdap_save_groups(TALLOC_CTX *memctx,
         goto done;
     }
 
-    if (_timestamp) {
-        *_timestamp = talloc_steal(memctx, higher_timestamp);
+    if (_usn_value) {
+        *_usn_value = talloc_steal(memctx, higher_usn);
     }
 
 done:
@@ -1431,7 +1467,7 @@ struct sdap_get_groups_state {
     const char **attrs;
     const char *filter;
 
-    char *higher_timestamp;
+    char *higher_usn;
     struct sysdb_attrs **groups;
     size_t count;
     size_t check_count;
@@ -1465,7 +1501,7 @@ struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
     state->sysdb = sysdb;
     state->filter = filter;
     state->attrs = attrs;
-    state->higher_timestamp = NULL;
+    state->higher_usn = NULL;
     state->groups =  NULL;
     state->count = 0;
 
@@ -1604,7 +1640,7 @@ static void sdap_get_groups_done(struct tevent_req *subreq)
 
         ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
                                state->groups, state->count, true,
-                               &state->higher_timestamp);
+                               &state->higher_usn);
         if (ret) {
             DEBUG(2, ("Failed to store groups.\n"));
             tevent_req_error(req, ret);
@@ -1616,15 +1652,15 @@ static void sdap_get_groups_done(struct tevent_req *subreq)
 }
 
 int sdap_get_groups_recv(struct tevent_req *req,
-                         TALLOC_CTX *mem_ctx, char **timestamp)
+                         TALLOC_CTX *mem_ctx, char **usn_value)
 {
     struct sdap_get_groups_state *state = tevent_req_data(req,
                                             struct sdap_get_groups_state);
 
     TEVENT_REQ_RETURN_ON_ERROR(req);
 
-    if (timestamp) {
-        *timestamp = talloc_steal(mem_ctx, state->higher_timestamp);
+    if (usn_value) {
+        *usn_value = talloc_steal(mem_ctx, state->higher_usn);
     }
 
     return EOK;
@@ -1676,7 +1712,7 @@ static void sdap_nested_done(struct tevent_req *subreq)
      * place for the groups to add them.
      */
     ret = sdap_save_users(state, state->sysdb, state->dom, state->opts,
-                          users, count, &state->higher_timestamp);
+                          users, count, &state->higher_usn);
     if (ret != EOK) {
         tevent_req_error(req, ret);
         return;
@@ -1702,7 +1738,7 @@ static void sdap_nested_done(struct tevent_req *subreq)
     talloc_zfree(values);
 
     ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
-                           groups, count, false, &state->higher_timestamp);
+                           groups, count, false, &state->higher_usn);
     if (ret != EOK) {
         tevent_req_error(req, ret);
         return;
diff --git a/src/providers/ldap/sdap_id_op.c b/src/providers/ldap/sdap_id_op.c
index c4194efbcabcd7b70eb9dad3114badd66352a717..c38a803ffd7c9fd658c240156fc45cd72750ebc7 100644
--- a/src/providers/ldap/sdap_id_op.c
+++ b/src/providers/ldap/sdap_id_op.c
@@ -497,7 +497,7 @@ 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_cache *conn_cache = conn_data->conn_cache;
-    struct sdap_server_opts *srv_opts;
+    struct sdap_server_opts *srv_opts = NULL;
     bool can_retry = false;
     bool is_offline = false;
     int ret;
@@ -528,6 +528,7 @@ static void sdap_id_op_connect_done(struct tevent_req *subreq)
 
     if (ret == EOK) {
         ret = sdap_id_conn_data_set_expire_timer(conn_data);
+        sdap_steal_server_opts(conn_cache->id_ctx, &srv_opts);
     }
 
     if (can_retry) {
-- 
1.7.3.2

