Blame SOURCES/0081-LDAP-Detect-the-presence-of-POSIX-attributes.patch

2fc102
From 3b0d429051648a1545de528ec760c4823088a1d9 Mon Sep 17 00:00:00 2001
2fc102
From: Jakub Hrozek <jhrozek@redhat.com>
2fc102
Date: Mon, 16 Dec 2013 18:36:12 +0100
2fc102
Subject: [PATCH 81/84] LDAP: Detect the presence of POSIX attributes
2fc102
MIME-Version: 1.0
2fc102
Content-Type: text/plain; charset=UTF-8
2fc102
Content-Transfer-Encoding: 8bit
2fc102
2fc102
When the schema is set to AD and ID mapping is not used, there is a one-time
2fc102
check ran when searching for users to detect the presence of POSIX
2fc102
attributes in LDAP. If this check fails, the search fails as if no entry
2fc102
was found and returns a special error code.
2fc102
2fc102
The sdap_server_opts structure is filled every time a client connects to
2fc102
a server so the posix check boolean is reset to false again on connecting
2fc102
to the server.
2fc102
2fc102
It might be better to move the check to where the rootDSE is retrieved,
2fc102
but the check depends on several features that are not known to the code
2fc102
that retrieves the rootDSE (or the connection code for example) such as what
2fc102
the attribute mappings are or the authentication method that should be used.
2fc102
2fc102
Reviewed-by: Sumit Bose <sbose@redhat.com>
2fc102
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
2fc102
(cherry picked from commit e81deec535d11912b87954c81a1edd768c1386c9)
2fc102
---
2fc102
 src/providers/ad/ad_id.c              |  50 ++++++++-
2fc102
 src/providers/ad/ad_id.h              |   1 +
2fc102
 src/providers/ipa/ipa_subdomains_id.c |   2 +-
2fc102
 src/providers/ldap/ldap_id.c          | 158 +++++++++++++++++++++++++--
2fc102
 src/providers/ldap/sdap.h             |   1 +
2fc102
 src/providers/ldap/sdap_async.c       | 200 ++++++++++++++++++++++++++++++++++
2fc102
 src/providers/ldap/sdap_async.h       |   9 ++
2fc102
 src/providers/ldap/sdap_async_enum.c  |  96 +++++++++++++++-
2fc102
 src/util/util_errors.c                |   1 +
2fc102
 src/util/util_errors.h                |   1 +
2fc102
 10 files changed, 504 insertions(+), 15 deletions(-)
2fc102
2fc102
diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
2fc102
index e3302c15774ab1c24b16cefc274313e447b31e5c..bfae86284355b6c13547aac55b7273133bde851d 100644
2fc102
--- a/src/providers/ad/ad_id.c
2fc102
+++ b/src/providers/ad/ad_id.c
2fc102
@@ -27,6 +27,28 @@
2fc102
 #include "providers/ldap/sdap_async_enum.h"
2fc102
 #include "providers/ldap/sdap_idmap.h"
2fc102
 
2fc102
+static void
2fc102
+disable_gc(struct ad_options *ad_options)
2fc102
+{
2fc102
+    errno_t ret;
2fc102
+
2fc102
+    if (dp_opt_get_bool(ad_options->basic, AD_ENABLE_GC) == false) {
2fc102
+        return;
2fc102
+    }
2fc102
+
2fc102
+    DEBUG(SSSDBG_IMPORTANT_INFO, ("POSIX attributes were requested "
2fc102
+          "but are not present on the server side. Global Catalog "
2fc102
+          "lookups will be disabled\n"));
2fc102
+
2fc102
+    ret = dp_opt_set_bool(ad_options->basic,
2fc102
+                          AD_ENABLE_GC, false);
2fc102
+    if (ret != EOK) {
2fc102
+        DEBUG(SSSDBG_MINOR_FAILURE,
2fc102
+                ("Could not turn off GC support\n"));
2fc102
+        /* Not fatal */
2fc102
+    }
2fc102
+}
2fc102
+
2fc102
 struct ad_handle_acct_info_state {
2fc102
     struct be_req *breq;
2fc102
     struct be_acct_req *ar;
2fc102
@@ -34,6 +56,7 @@ struct ad_handle_acct_info_state {
2fc102
     struct sdap_id_conn_ctx **conn;
2fc102
     struct sdap_domain *sdom;
2fc102
     size_t cindex;
2fc102
+    struct ad_options *ad_options;
2fc102
 
2fc102
     int dp_error;
2fc102
     const char *err;
2fc102
@@ -47,6 +70,7 @@ ad_handle_acct_info_send(TALLOC_CTX *mem_ctx,
2fc102
                          struct be_req *breq,
2fc102
                          struct be_acct_req *ar,
2fc102
                          struct sdap_id_ctx *ctx,
2fc102
+                         struct ad_options *ad_options,
2fc102
                          struct sdap_domain *sdom,
2fc102
                          struct sdap_id_conn_ctx **conn)
2fc102
 {
2fc102
@@ -64,6 +88,7 @@ ad_handle_acct_info_send(TALLOC_CTX *mem_ctx,
2fc102
     state->ctx = ctx;
2fc102
     state->sdom = sdom;
2fc102
     state->conn = conn;
2fc102
+    state->ad_options = ad_options;
2fc102
     state->cindex = 0;
2fc102
 
2fc102
     ret = ad_handle_acct_info_step(req);
2fc102
@@ -137,12 +162,14 @@ ad_handle_acct_info_done(struct tevent_req *subreq)
2fc102
     if (sdap_err == EOK) {
2fc102
         tevent_req_done(req);
2fc102
         return;
2fc102
+    } else if (sdap_err == ERR_NO_POSIX) {
2fc102
+        disable_gc(state->ad_options);
2fc102
     } else if (sdap_err != ENOENT) {
2fc102
         tevent_req_error(req, EIO);
2fc102
         return;
2fc102
     }
2fc102
 
2fc102
-    /* Ret is only ENOENT now. Try the next connection */
2fc102
+    /* Ret is only ENOENT or ERR_NO_POSIX now. Try the next connection */
2fc102
     state->cindex++;
2fc102
     ret = ad_handle_acct_info_step(req);
2fc102
     if (ret != EAGAIN) {
2fc102
@@ -356,7 +383,7 @@ ad_account_info_handler(struct be_req *be_req)
2fc102
     }
2fc102
 
2fc102
     req = ad_handle_acct_info_send(be_req, be_req, ar, sdap_id_ctx,
2fc102
-                                   sdom, clist);
2fc102
+                                   ad_ctx->ad_options, sdom, clist);
2fc102
     if (req == NULL) {
2fc102
         ret = ENOMEM;
2fc102
         goto fail;
2fc102
@@ -611,9 +638,24 @@ ad_enumeration_done(struct tevent_req *subreq)
2fc102
 
2fc102
     ret = sdap_dom_enum_ex_recv(subreq);
2fc102
     talloc_zfree(subreq);
2fc102
-    if (ret != EOK) {
2fc102
+    if (ret == ERR_NO_POSIX) {
2fc102
+        /* Retry enumerating the same domain again, this time w/o
2fc102
+         * connecting to GC
2fc102
+         */
2fc102
+        disable_gc(state->id_ctx->ad_options);
2fc102
+        ret = ad_enum_sdom(req, state->sditer, state->id_ctx);
2fc102
+        if (ret != EOK) {
2fc102
+            DEBUG(SSSDBG_OP_FAILURE,
2fc102
+                ("Could not retry domain %s\n", state->sditer->dom->name));
2fc102
+            tevent_req_error(req, ret);
2fc102
+            return;
2fc102
+        }
2fc102
+
2fc102
+        /* Execution will resume in ad_enumeration_done */
2fc102
+        return;
2fc102
+    } else if (ret != EOK) {
2fc102
         DEBUG(SSSDBG_OP_FAILURE,
2fc102
-              ("Could not enumerate domain %s\n", state->sdom->dom->name));
2fc102
+              ("Could not enumerate domain %s\n", state->sditer->dom->name));
2fc102
         tevent_req_error(req, ret);
2fc102
         return;
2fc102
     }
2fc102
diff --git a/src/providers/ad/ad_id.h b/src/providers/ad/ad_id.h
2fc102
index 74b85645c2d6458617a4064fe3fb3f99696c3741..9eb0ac3754be2fd687ed2257b91854f5fceb82a2 100644
2fc102
--- a/src/providers/ad/ad_id.h
2fc102
+++ b/src/providers/ad/ad_id.h
2fc102
@@ -31,6 +31,7 @@ ad_handle_acct_info_send(TALLOC_CTX *mem_ctx,
2fc102
                          struct be_req *breq,
2fc102
                          struct be_acct_req *ar,
2fc102
                          struct sdap_id_ctx *ctx,
2fc102
+                         struct ad_options *ad_options,
2fc102
                          struct sdap_domain *sdom,
2fc102
                          struct sdap_id_conn_ctx **conn);
2fc102
 errno_t
2fc102
diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
2fc102
index fb1ad896885866dd9c34f9db960e09d92763f86d..b61c6a5f4d7605f0cdfa182bbc933d35c4613a79 100644
2fc102
--- a/src/providers/ipa/ipa_subdomains_id.c
2fc102
+++ b/src/providers/ipa/ipa_subdomains_id.c
2fc102
@@ -323,7 +323,7 @@ ipa_get_ad_acct_send(TALLOC_CTX *mem_ctx,
2fc102
     }
2fc102
 
2fc102
     subreq = ad_handle_acct_info_send(req, be_req, ar, sdap_id_ctx,
2fc102
-                                      sdom, clist);
2fc102
+                                      ad_id_ctx->ad_options, sdom, clist);
2fc102
     if (subreq == NULL) {
2fc102
         ret = ENOMEM;
2fc102
         goto fail;
2fc102
diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
2fc102
index e36c1f697c18e865a47d991dad103fc440456118..b948ba9f358af994bdbbb99a468f7dbe66e65c1d 100644
2fc102
--- a/src/providers/ldap/ldap_id.c
2fc102
+++ b/src/providers/ldap/ldap_id.c
2fc102
@@ -50,6 +50,7 @@ struct users_get_state {
2fc102
 
2fc102
     char *filter;
2fc102
     const char **attrs;
2fc102
+    bool use_id_mapping;
2fc102
 
2fc102
     int dp_error;
2fc102
     int sdap_ret;
2fc102
@@ -58,6 +59,8 @@ struct users_get_state {
2fc102
 
2fc102
 static int users_get_retry(struct tevent_req *req);
2fc102
 static void users_get_connect_done(struct tevent_req *subreq);
2fc102
+static void users_get_posix_check_done(struct tevent_req *subreq);
2fc102
+static void users_get_search(struct tevent_req *req);
2fc102
 static void users_get_done(struct tevent_req *subreq);
2fc102
 
2fc102
 struct tevent_req *users_get_send(TALLOC_CTX *memctx,
2fc102
@@ -79,7 +82,6 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
2fc102
     uid_t uid;
2fc102
     enum idmap_error_code err;
2fc102
     char *sid;
2fc102
-    bool use_id_mapping;
2fc102
 
2fc102
     req = tevent_req_create(memctx, &state, struct users_get_state);
2fc102
     if (!req) return NULL;
2fc102
@@ -103,7 +105,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
2fc102
     state->name = name;
2fc102
     state->filter_type = filter_type;
2fc102
 
2fc102
-    use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
2fc102
+    state->use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
2fc102
                                                           ctx->opts->idmap_ctx,
2fc102
                                                           sdom->dom->name,
2fc102
                                                           sdom->dom->domain_id);
2fc102
@@ -116,7 +118,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
2fc102
         }
2fc102
         break;
2fc102
     case BE_FILTER_IDNUM:
2fc102
-        if (use_id_mapping) {
2fc102
+        if (state->use_id_mapping) {
2fc102
             /* If we're ID-mapping, we need to use the objectSID
2fc102
              * in the search filter.
2fc102
              */
2fc102
@@ -184,7 +186,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
2fc102
         goto fail;
2fc102
     }
2fc102
 
2fc102
-    if (use_id_mapping || filter_type == BE_FILTER_SECID) {
2fc102
+    if (state->use_id_mapping || filter_type == BE_FILTER_SECID) {
2fc102
         /* When mapping IDs or looking for SIDs, we don't want to limit
2fc102
          * ourselves to users with a UID value. But there must be a SID to map
2fc102
          * from.
2fc102
@@ -269,6 +271,75 @@ static void users_get_connect_done(struct tevent_req *subreq)
2fc102
         return;
2fc102
     }
2fc102
 
2fc102
+    /* If POSIX attributes have been requested with an AD server and we
2fc102
+     * have no idea about POSIX attributes support, run a one-time check
2fc102
+     */
2fc102
+    if (state->use_id_mapping == false &&
2fc102
+            state->ctx->opts->schema_type == SDAP_SCHEMA_AD &&
2fc102
+            state->ctx->srv_opts &&
2fc102
+            state->ctx->srv_opts->posix_checked == false) {
2fc102
+        subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
2fc102
+                                       sdap_id_op_handle(state->op),
2fc102
+                                       state->sdom->user_search_bases,
2fc102
+                                       dp_opt_get_int(state->ctx->opts->basic,
2fc102
+                                                      SDAP_SEARCH_TIMEOUT));
2fc102
+        if (subreq == NULL) {
2fc102
+            tevent_req_error(req, ENOMEM);
2fc102
+            return;
2fc102
+        }
2fc102
+        tevent_req_set_callback(subreq, users_get_posix_check_done, req);
2fc102
+        return;
2fc102
+    }
2fc102
+
2fc102
+    users_get_search(req);
2fc102
+}
2fc102
+
2fc102
+static void users_get_posix_check_done(struct tevent_req *subreq)
2fc102
+{
2fc102
+    errno_t ret;
2fc102
+    bool has_posix;
2fc102
+    int dp_error;
2fc102
+    struct tevent_req *req = tevent_req_callback_data(subreq,
2fc102
+                                                      struct tevent_req);
2fc102
+    struct users_get_state *state = tevent_req_data(req,
2fc102
+                                                    struct users_get_state);
2fc102
+
2fc102
+    ret = sdap_posix_check_recv(subreq, &has_posix);
2fc102
+    talloc_zfree(subreq);
2fc102
+    if (ret != EOK) {
2fc102
+        /* We can only finish the id_op on error as the connection
2fc102
+         * is re-used by the user search
2fc102
+         */
2fc102
+        ret = sdap_id_op_done(state->op, ret, &dp_error);
2fc102
+        if (dp_error == DP_ERR_OK && ret != EOK) {
2fc102
+            /* retry */
2fc102
+            ret = users_get_retry(req);
2fc102
+            if (ret != EOK) {
2fc102
+                tevent_req_error(req, ret);
2fc102
+            }
2fc102
+            return;
2fc102
+        }
2fc102
+    }
2fc102
+
2fc102
+    state->ctx->srv_opts->posix_checked = true;
2fc102
+
2fc102
+    /* If the check ran to completion, we know for certain about the attributes
2fc102
+     */
2fc102
+    if (has_posix == false) {
2fc102
+        state->sdap_ret = ERR_NO_POSIX;
2fc102
+        tevent_req_done(req);
2fc102
+        return;
2fc102
+    }
2fc102
+
2fc102
+    users_get_search(req);
2fc102
+}
2fc102
+
2fc102
+static void users_get_search(struct tevent_req *req)
2fc102
+{
2fc102
+    struct users_get_state *state = tevent_req_data(req,
2fc102
+                                                     struct users_get_state);
2fc102
+    struct tevent_req *subreq;
2fc102
+
2fc102
     subreq = sdap_get_users_send(state, state->ev,
2fc102
                                  state->domain, state->sysdb,
2fc102
                                  state->ctx->opts,
2fc102
@@ -434,6 +505,7 @@ struct groups_get_state {
2fc102
 
2fc102
     char *filter;
2fc102
     const char **attrs;
2fc102
+    bool use_id_mapping;
2fc102
 
2fc102
     int dp_error;
2fc102
     int sdap_ret;
2fc102
@@ -442,6 +514,8 @@ struct groups_get_state {
2fc102
 
2fc102
 static int groups_get_retry(struct tevent_req *req);
2fc102
 static void groups_get_connect_done(struct tevent_req *subreq);
2fc102
+static void groups_get_posix_check_done(struct tevent_req *subreq);
2fc102
+static void groups_get_search(struct tevent_req *req);
2fc102
 static void groups_get_done(struct tevent_req *subreq);
2fc102
 
2fc102
 struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
2fc102
@@ -463,7 +537,6 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
2fc102
     gid_t gid;
2fc102
     enum idmap_error_code err;
2fc102
     char *sid;
2fc102
-    bool use_id_mapping;
2fc102
     const char *member_filter[2];
2fc102
 
2fc102
     req = tevent_req_create(memctx, &state, struct groups_get_state);
2fc102
@@ -488,7 +561,7 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
2fc102
     state->name = name;
2fc102
     state->filter_type = filter_type;
2fc102
 
2fc102
-    use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
2fc102
+    state->use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
2fc102
                                                           ctx->opts->idmap_ctx,
2fc102
                                                           sdom->dom->name,
2fc102
                                                           sdom->dom->domain_id);
2fc102
@@ -503,7 +576,7 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
2fc102
         }
2fc102
         break;
2fc102
     case BE_FILTER_IDNUM:
2fc102
-        if (use_id_mapping) {
2fc102
+        if (state->use_id_mapping) {
2fc102
             /* If we're ID-mapping, we need to use the objectSID
2fc102
              * in the search filter.
2fc102
              */
2fc102
@@ -571,7 +644,7 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
2fc102
         goto fail;
2fc102
     }
2fc102
 
2fc102
-    if (use_id_mapping || filter_type == BE_FILTER_SECID) {
2fc102
+    if (state->use_id_mapping || filter_type == BE_FILTER_SECID) {
2fc102
         /* When mapping IDs or looking for SIDs, we don't want to limit
2fc102
          * ourselves to groups with a GID value
2fc102
          */
2fc102
@@ -660,6 +733,75 @@ static void groups_get_connect_done(struct tevent_req *subreq)
2fc102
         return;
2fc102
     }
2fc102
 
2fc102
+    /* If POSIX attributes have been requested with an AD server and we
2fc102
+     * have no idea about POSIX attributes support, run a one-time check
2fc102
+     */
2fc102
+    if (state->use_id_mapping == false &&
2fc102
+            state->ctx->opts->schema_type == SDAP_SCHEMA_AD &&
2fc102
+            state->ctx->srv_opts &&
2fc102
+            state->ctx->srv_opts->posix_checked == false) {
2fc102
+        subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
2fc102
+                                       sdap_id_op_handle(state->op),
2fc102
+                                       state->sdom->user_search_bases,
2fc102
+                                       dp_opt_get_int(state->ctx->opts->basic,
2fc102
+                                                      SDAP_SEARCH_TIMEOUT));
2fc102
+        if (subreq == NULL) {
2fc102
+            tevent_req_error(req, ENOMEM);
2fc102
+            return;
2fc102
+        }
2fc102
+        tevent_req_set_callback(subreq, groups_get_posix_check_done, req);
2fc102
+        return;
2fc102
+    }
2fc102
+
2fc102
+    groups_get_search(req);
2fc102
+}
2fc102
+
2fc102
+static void groups_get_posix_check_done(struct tevent_req *subreq)
2fc102
+{
2fc102
+    errno_t ret;
2fc102
+    bool has_posix;
2fc102
+    int dp_error;
2fc102
+    struct tevent_req *req = tevent_req_callback_data(subreq,
2fc102
+                                                      struct tevent_req);
2fc102
+    struct groups_get_state *state = tevent_req_data(req,
2fc102
+                                                     struct groups_get_state);
2fc102
+
2fc102
+    ret = sdap_posix_check_recv(subreq, &has_posix);
2fc102
+    talloc_zfree(subreq);
2fc102
+    if (ret != EOK) {
2fc102
+        /* We can only finish the id_op on error as the connection
2fc102
+         * is re-used by the group search
2fc102
+         */
2fc102
+        ret = sdap_id_op_done(state->op, ret, &dp_error);
2fc102
+        if (dp_error == DP_ERR_OK && ret != EOK) {
2fc102
+            /* retry */
2fc102
+            ret = groups_get_retry(req);
2fc102
+            if (ret != EOK) {
2fc102
+                tevent_req_error(req, ret);
2fc102
+            }
2fc102
+            return;
2fc102
+        }
2fc102
+    }
2fc102
+
2fc102
+    state->ctx->srv_opts->posix_checked = true;
2fc102
+
2fc102
+    /* If the check ran to completion, we know for certain about the attributes
2fc102
+     */
2fc102
+    if (has_posix == false) {
2fc102
+        state->sdap_ret = ERR_NO_POSIX;
2fc102
+        tevent_req_done(req);
2fc102
+        return;
2fc102
+    }
2fc102
+
2fc102
+    groups_get_search(req);
2fc102
+}
2fc102
+
2fc102
+static void groups_get_search(struct tevent_req *req)
2fc102
+{
2fc102
+    struct groups_get_state *state = tevent_req_data(req,
2fc102
+                                                     struct groups_get_state);
2fc102
+    struct tevent_req *subreq;
2fc102
+
2fc102
     subreq = sdap_get_groups_send(state, state->ev,
2fc102
                                   state->sdom,
2fc102
                                   state->ctx->opts,
2fc102
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
2fc102
index d408be0a65cdd840d8379b7af4c0ab1e67ed3f5c..f3f13e9c72b368af0c14e2d830a89a41e8cf77e4 100644
2fc102
--- a/src/providers/ldap/sdap.h
2fc102
+++ b/src/providers/ldap/sdap.h
2fc102
@@ -446,6 +446,7 @@ struct sdap_server_opts {
2fc102
     char *max_group_value;
2fc102
     char *max_service_value;
2fc102
     char *max_sudo_value;
2fc102
+    bool posix_checked;
2fc102
 };
2fc102
 
2fc102
 struct sdap_id_ctx;
2fc102
diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
2fc102
index 367007bde0011ed4de283b2a50b22538830a5275..1022a093f06ec7e9a50b13160fc9a4660a255e92 100644
2fc102
--- a/src/providers/ldap/sdap_async.c
2fc102
+++ b/src/providers/ldap/sdap_async.c
2fc102
@@ -21,6 +21,7 @@
2fc102
 
2fc102
 #include <ctype.h>
2fc102
 #include "util/util.h"
2fc102
+#include "util/strtonum.h"
2fc102
 #include "providers/ldap/sdap_async_private.h"
2fc102
 
2fc102
 #define REALM_SEPARATOR '@'
2fc102
@@ -2083,6 +2084,205 @@ int sdap_asq_search_recv(struct tevent_req *req,
2fc102
     return EOK;
2fc102
 }
2fc102
 
2fc102
+/* ==Posix attribute presence test================================= */
2fc102
+static errno_t sdap_posix_check_next(struct tevent_req *req);
2fc102
+static void sdap_posix_check_done(struct tevent_req *subreq);
2fc102
+static errno_t sdap_posix_check_parse(struct sdap_handle *sh,
2fc102
+                                      struct sdap_msg *msg,
2fc102
+                                      void *pvt);
2fc102
+
2fc102
+struct sdap_posix_check_state {
2fc102
+    struct tevent_context *ev;
2fc102
+    struct sdap_options *opts;
2fc102
+    struct sdap_handle *sh;
2fc102
+    struct sdap_search_base **search_bases;
2fc102
+    int timeout;
2fc102
+
2fc102
+    const char **attrs;
2fc102
+    const char *filter;
2fc102
+    size_t base_iter;
2fc102
+
2fc102
+    bool has_posix;
2fc102
+};
2fc102
+
2fc102
+struct tevent_req *
2fc102
+sdap_posix_check_send(TALLOC_CTX *memctx, struct tevent_context *ev,
2fc102
+                      struct sdap_options *opts, struct sdap_handle *sh,
2fc102
+                      struct sdap_search_base **search_bases,
2fc102
+                      int timeout)
2fc102
+{
2fc102
+    struct tevent_req *req = NULL;
2fc102
+    struct sdap_posix_check_state *state;
2fc102
+    errno_t ret;
2fc102
+
2fc102
+    req = tevent_req_create(memctx, &state, struct sdap_posix_check_state);
2fc102
+    if (req == NULL) {
2fc102
+        return NULL;
2fc102
+    }
2fc102
+    state->ev = ev;
2fc102
+    state->sh = sh;
2fc102
+    state->opts = opts;
2fc102
+    state->search_bases = search_bases;
2fc102
+    state->timeout = timeout;
2fc102
+
2fc102
+    state->attrs = talloc_array(state, const char *, 4);
2fc102
+    if (state->attrs == NULL) {
2fc102
+        ret = ENOMEM;
2fc102
+        goto fail;
2fc102
+    }
2fc102
+    state->attrs[0] = "objectclass";
2fc102
+    state->attrs[1] = opts->user_map[SDAP_AT_USER_UID].name;
2fc102
+    state->attrs[2] = opts->group_map[SDAP_AT_GROUP_GID].name;
2fc102
+    state->attrs[3] = NULL;
2fc102
+
2fc102
+    state->filter = talloc_asprintf(state, "(|(%s=*)(%s=*))",
2fc102
+                                    opts->user_map[SDAP_AT_USER_UID].name,
2fc102
+                                    opts->group_map[SDAP_AT_GROUP_GID].name);
2fc102
+    if (state->filter == NULL) {
2fc102
+        ret = ENOMEM;
2fc102
+        goto fail;
2fc102
+    }
2fc102
+
2fc102
+    ret = sdap_posix_check_next(req);
2fc102
+    if (ret != EOK) {
2fc102
+        goto fail;
2fc102
+    }
2fc102
+
2fc102
+    return req;
2fc102
+
2fc102
+fail:
2fc102
+    tevent_req_error(req, ret);
2fc102
+    tevent_req_post(req, ev);
2fc102
+    return req;
2fc102
+}
2fc102
+
2fc102
+static errno_t sdap_posix_check_next(struct tevent_req *req)
2fc102
+{
2fc102
+    struct tevent_req *subreq = NULL;
2fc102
+    struct sdap_posix_check_state *state =
2fc102
+        tevent_req_data(req, struct sdap_posix_check_state);
2fc102
+
2fc102
+    DEBUG(SSSDBG_TRACE_FUNC,
2fc102
+          ("Searching for POSIX attributes with base [%s]\n",
2fc102
+           state->search_bases[state->base_iter]->basedn));
2fc102
+
2fc102
+    subreq = sdap_get_generic_ext_send(state, state->ev, state->opts,
2fc102
+                                 state->sh,
2fc102
+                                 state->search_bases[state->base_iter]->basedn,
2fc102
+                                 LDAP_SCOPE_SUBTREE, state->filter,
2fc102
+                                 state->attrs, false,
2fc102
+                                 NULL, NULL, 1, state->timeout,
2fc102
+                                 false, sdap_posix_check_parse,
2fc102
+                                 state);
2fc102
+    if (subreq == NULL) {
2fc102
+        return ENOMEM;
2fc102
+    }
2fc102
+    tevent_req_set_callback(subreq, sdap_posix_check_done, req);
2fc102
+
2fc102
+    return EOK;
2fc102
+}
2fc102
+
2fc102
+static errno_t sdap_posix_check_parse(struct sdap_handle *sh,
2fc102
+                                      struct sdap_msg *msg,
2fc102
+                                      void *pvt)
2fc102
+{
2fc102
+    struct berval **vals = NULL;
2fc102
+    struct sdap_posix_check_state *state =
2fc102
+        talloc_get_type(pvt, struct sdap_posix_check_state);
2fc102
+    char *dn;
2fc102
+    char *endptr;
2fc102
+
2fc102
+    dn = ldap_get_dn(sh->ldap, msg->msg);
2fc102
+    if (dn == NULL) {
2fc102
+        DEBUG(SSSDBG_TRACE_LIBS,
2fc102
+              ("Search did not find any entry with POSIX attributes\n"));
2fc102
+        goto done;
2fc102
+    }
2fc102
+    DEBUG(SSSDBG_TRACE_LIBS, ("Found [%s] with POSIX attributes\n", dn));
2fc102
+    ldap_memfree(dn);
2fc102
+
2fc102
+    vals = ldap_get_values_len(sh->ldap, msg->msg,
2fc102
+                               state->opts->user_map[SDAP_AT_USER_UID].name);
2fc102
+    if (vals == NULL) {
2fc102
+        vals = ldap_get_values_len(sh->ldap, msg->msg,
2fc102
+                               state->opts->group_map[SDAP_AT_GROUP_GID].name);
2fc102
+        if (vals == NULL) {
2fc102
+            DEBUG(SSSDBG_TRACE_LIBS, ("Entry does not have POSIX attrs?\n"));
2fc102
+            goto done;
2fc102
+        }
2fc102
+    }
2fc102
+
2fc102
+    if (vals[0] == NULL) {
2fc102
+        DEBUG(SSSDBG_TRACE_LIBS, ("No value for POSIX attr\n"));
2fc102
+        goto done;
2fc102
+    }
2fc102
+
2fc102
+    errno = 0;
2fc102
+    strtouint32(vals[0]->bv_val, &endptr, 10);
2fc102
+    if (errno || *endptr || (vals[0]->bv_val == endptr)) {
2fc102
+        DEBUG(SSSDBG_OP_FAILURE,
2fc102
+              ("POSIX attribute is not a number: %s\n", vals[0]->bv_val));
2fc102
+        goto done;
2fc102
+    }
2fc102
+
2fc102
+    state->has_posix = true;
2fc102
+done:
2fc102
+    ldap_value_free_len(vals);
2fc102
+    return EOK;
2fc102
+}
2fc102
+
2fc102
+static void sdap_posix_check_done(struct tevent_req *subreq)
2fc102
+{
2fc102
+    struct tevent_req *req = tevent_req_callback_data(subreq,
2fc102
+                                                      struct tevent_req);
2fc102
+    struct sdap_posix_check_state *state =
2fc102
+        tevent_req_data(req, struct sdap_posix_check_state);
2fc102
+    errno_t ret;
2fc102
+
2fc102
+    ret = sdap_get_generic_ext_recv(subreq);
2fc102
+    talloc_zfree(subreq);
2fc102
+    if (ret != EOK) {
2fc102
+        DEBUG(SSSDBG_OP_FAILURE,
2fc102
+              ("sdap_get_generic_ext_recv failed [%d]: %s\n",
2fc102
+              ret, strerror(ret)));
2fc102
+        tevent_req_error(req, ret);
2fc102
+        return;
2fc102
+    }
2fc102
+
2fc102
+    /* Positive hit is definitve, no need to search other bases */
2fc102
+    if (state->has_posix == true) {
2fc102
+        DEBUG(SSSDBG_FUNC_DATA, ("Server has POSIX attributes\n"));
2fc102
+        tevent_req_done(req);
2fc102
+        return;
2fc102
+    }
2fc102
+
2fc102
+    state->base_iter++;
2fc102
+    if (state->search_bases[state->base_iter]) {
2fc102
+        /* There are more search bases to try */
2fc102
+        ret = sdap_posix_check_next(req);
2fc102
+        if (ret != EOK) {
2fc102
+            tevent_req_error(req, ret);
2fc102
+        }
2fc102
+        return;
2fc102
+    }
2fc102
+
2fc102
+    /* All bases done! */
2fc102
+    DEBUG(SSSDBG_TRACE_LIBS, ("Cycled through all bases\n"));
2fc102
+    tevent_req_done(req);
2fc102
+}
2fc102
+
2fc102
+int sdap_posix_check_recv(struct tevent_req *req,
2fc102
+                          bool *_has_posix)
2fc102
+{
2fc102
+    struct sdap_posix_check_state *state = tevent_req_data(req,
2fc102
+                                            struct sdap_posix_check_state);
2fc102
+
2fc102
+    TEVENT_REQ_RETURN_ON_ERROR(req);
2fc102
+
2fc102
+    *_has_posix = state->has_posix;
2fc102
+    return EOK;
2fc102
+}
2fc102
+
2fc102
 /* ==Generic Deref Search============================================ */
2fc102
 enum sdap_deref_type {
2fc102
     SDAP_DEREF_OPENLDAP,
2fc102
diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
2fc102
index 33e8708ab7e80ab4280df300fdc300d4ecd18305..593404af3c460f8d956b0832b8f7e398b331729b 100644
2fc102
--- a/src/providers/ldap/sdap_async.h
2fc102
+++ b/src/providers/ldap/sdap_async.h
2fc102
@@ -210,6 +210,15 @@ int sdap_deref_search_recv(struct tevent_req *req,
2fc102
                            size_t *reply_count,
2fc102
                            struct sdap_deref_attrs ***reply);
2fc102
 
2fc102
+struct tevent_req *
2fc102
+sdap_posix_check_send(TALLOC_CTX *memctx, struct tevent_context *ev,
2fc102
+                      struct sdap_options *opts, struct sdap_handle *sh,
2fc102
+                      struct sdap_search_base **search_bases,
2fc102
+                      int timeout);
2fc102
+
2fc102
+int sdap_posix_check_recv(struct tevent_req *req,
2fc102
+                          bool *_has_posix);
2fc102
+
2fc102
 errno_t
2fc102
 sdap_attrs_add_ldap_attr(struct sysdb_attrs *ldap_attrs,
2fc102
                          const char *attr_name,
2fc102
diff --git a/src/providers/ldap/sdap_async_enum.c b/src/providers/ldap/sdap_async_enum.c
2fc102
index cbc56be20526e6c2323f9fd1b49038dd4bf13fe5..0c20afa9d7a1b0198bb71cffdafcb14763c1dafb 100644
2fc102
--- a/src/providers/ldap/sdap_async_enum.c
2fc102
+++ b/src/providers/ldap/sdap_async_enum.c
2fc102
@@ -68,6 +68,8 @@ static errno_t sdap_dom_enum_ex_retry(struct tevent_req *req,
2fc102
                                       tevent_req_fn tcb);
2fc102
 static bool sdap_dom_enum_ex_connected(struct tevent_req *subreq);
2fc102
 static void sdap_dom_enum_ex_get_users(struct tevent_req *subreq);
2fc102
+static void sdap_dom_enum_ex_posix_check_done(struct tevent_req *subreq);
2fc102
+static errno_t sdap_dom_enum_search_users(struct tevent_req *req);
2fc102
 static void sdap_dom_enum_ex_users_done(struct tevent_req *subreq);
2fc102
 static void sdap_dom_enum_ex_get_groups(struct tevent_req *subreq);
2fc102
 static void sdap_dom_enum_ex_groups_done(struct tevent_req *subreq);
2fc102
@@ -178,19 +180,109 @@ static void sdap_dom_enum_ex_get_users(struct tevent_req *subreq)
2fc102
                                                       struct tevent_req);
2fc102
     struct sdap_dom_enum_ex_state *state = tevent_req_data(req,
2fc102
                                                 struct sdap_dom_enum_ex_state);
2fc102
+    bool use_id_mapping;
2fc102
+    errno_t ret;
2fc102
 
2fc102
     if (sdap_dom_enum_ex_connected(subreq) == false) {
2fc102
         return;
2fc102
     }
2fc102
 
2fc102
+    use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping(
2fc102
+                                            state->ctx->opts->idmap_ctx,
2fc102
+                                            state->sdom->dom->name,
2fc102
+                                            state->sdom->dom->domain_id);
2fc102
+
2fc102
+    /* If POSIX attributes have been requested with an AD server and we
2fc102
+     * have no idea about POSIX attributes support, run a one-time check
2fc102
+     */
2fc102
+    if (use_id_mapping == false &&
2fc102
+            state->ctx->opts->schema_type == SDAP_SCHEMA_AD &&
2fc102
+            state->ctx->srv_opts &&
2fc102
+            state->ctx->srv_opts->posix_checked == false) {
2fc102
+        subreq = sdap_posix_check_send(state, state->ev, state->ctx->opts,
2fc102
+                                       sdap_id_op_handle(state->user_op),
2fc102
+                                       state->sdom->user_search_bases,
2fc102
+                                       dp_opt_get_int(state->ctx->opts->basic,
2fc102
+                                                      SDAP_SEARCH_TIMEOUT));
2fc102
+        if (subreq == NULL) {
2fc102
+            tevent_req_error(req, ENOMEM);
2fc102
+            return;
2fc102
+        }
2fc102
+        tevent_req_set_callback(subreq,
2fc102
+                                sdap_dom_enum_ex_posix_check_done, req);
2fc102
+        return;
2fc102
+    }
2fc102
+
2fc102
+
2fc102
+    ret = sdap_dom_enum_search_users(req);
2fc102
+    if (ret != EOK) {
2fc102
+        tevent_req_error(req, ret);
2fc102
+        return;
2fc102
+    }
2fc102
+    /* Execution resumes in sdap_dom_enum_ex_users_done */
2fc102
+}
2fc102
+
2fc102
+static void sdap_dom_enum_ex_posix_check_done(struct tevent_req *subreq)
2fc102
+{
2fc102
+    errno_t ret;
2fc102
+    bool has_posix;
2fc102
+    int dp_error;
2fc102
+
2fc102
+    struct tevent_req *req = tevent_req_callback_data(subreq,
2fc102
+                                                      struct tevent_req);
2fc102
+    struct sdap_dom_enum_ex_state *state = tevent_req_data(req,
2fc102
+                                                struct sdap_dom_enum_ex_state);
2fc102
+
2fc102
+    ret = sdap_posix_check_recv(subreq, &has_posix);
2fc102
+    talloc_zfree(subreq);
2fc102
+    if (ret != EOK) {
2fc102
+        /* We can only finish the id_op on error as the connection
2fc102
+         * is re-used by the user search
2fc102
+         */
2fc102
+        ret = sdap_id_op_done(state->user_op, ret, &dp_error);
2fc102
+        if (dp_error == DP_ERR_OK && ret != EOK) {
2fc102
+            /* retry */
2fc102
+            ret = sdap_dom_enum_ex_retry(req, state->user_op,
2fc102
+                                         sdap_dom_enum_ex_get_users);
2fc102
+            if (ret != EOK) {
2fc102
+                tevent_req_error(req, ret);
2fc102
+            }
2fc102
+            return;
2fc102
+        }
2fc102
+    }
2fc102
+
2fc102
+    state->ctx->srv_opts->posix_checked = true;
2fc102
+
2fc102
+    /* If the check ran to completion, we know for certain about the attributes
2fc102
+     */
2fc102
+    if (has_posix == false) {
2fc102
+        tevent_req_error(req, ERR_NO_POSIX);
2fc102
+        return;
2fc102
+    }
2fc102
+
2fc102
+
2fc102
+    ret = sdap_dom_enum_search_users(req);
2fc102
+    if (ret != EOK) {
2fc102
+        tevent_req_error(req, ret);
2fc102
+        return;
2fc102
+    }
2fc102
+    /* Execution resumes in sdap_dom_enum_ex_users_done */
2fc102
+}
2fc102
+
2fc102
+static errno_t sdap_dom_enum_search_users(struct tevent_req *req)
2fc102
+{
2fc102
+    struct sdap_dom_enum_ex_state *state = tevent_req_data(req,
2fc102
+                                                struct sdap_dom_enum_ex_state);
2fc102
+    struct tevent_req *subreq;
2fc102
+
2fc102
     subreq = enum_users_send(state, state->ev,
2fc102
                              state->ctx, state->sdom,
2fc102
                              state->user_op, state->purge);
2fc102
     if (subreq == NULL) {
2fc102
-        tevent_req_error(req, ENOMEM);
2fc102
-        return;
2fc102
+        return ENOMEM;
2fc102
     }
2fc102
     tevent_req_set_callback(subreq, sdap_dom_enum_ex_users_done, req);
2fc102
+    return EOK;
2fc102
 }
2fc102
 
2fc102
 static void sdap_dom_enum_ex_users_done(struct tevent_req *subreq)
2fc102
diff --git a/src/util/util_errors.c b/src/util/util_errors.c
2fc102
index 633257e8da0ef039e555a07ad8b51125114ca01c..c9b507557da07555c719bb0dd18145e6799a53eb 100644
2fc102
--- a/src/util/util_errors.c
2fc102
+++ b/src/util/util_errors.c
2fc102
@@ -52,6 +52,7 @@ struct err_string error_to_str[] = {
2fc102
     { "Domain not found" }, /* ERR_DOMAIN_NOT_FOUND */
2fc102
     { "Missing configuration file" }, /* ERR_MISSING_CONF */
2fc102
     { "Malformed search filter" }, /* ERR_INVALID_FILTER, */
2fc102
+    { "No POSIX attributes detected" }, /* ERR_NO_POSIX */
2fc102
 };
2fc102
 
2fc102
 
2fc102
diff --git a/src/util/util_errors.h b/src/util/util_errors.h
2fc102
index 1332085031dbe6935cbdc94543fa14b09fe81028..3dd94af1f304d65e22515c859c6f69a021fa7e92 100644
2fc102
--- a/src/util/util_errors.h
2fc102
+++ b/src/util/util_errors.h
2fc102
@@ -74,6 +74,7 @@ enum sssd_errors {
2fc102
     ERR_DOMAIN_NOT_FOUND,
2fc102
     ERR_MISSING_CONF,
2fc102
     ERR_INVALID_FILTER,
2fc102
+    ERR_NO_POSIX,
2fc102
     ERR_LAST            /* ALWAYS LAST */
2fc102
 };
2fc102
 
2fc102
-- 
2fc102
1.8.5.3
2fc102