On Wed, 2015-05-27 at 11:15 +0200, Jakub Hrozek wrote:
> On Tue, May 26, 2015 at 03:56:35PM -0400, Stephen Gallagher wrote:
> > Sorry for the delay; two new patches attached.
> > 
> > This patch fixes the two missing error checks in the AD GPO code as
> > well as making several changes to the general LDAP support of
> > referrals.
> > 
> > While I was looking through this, I discovered a bug that resulted 
> > in
> > referrals for more information not being returned to the caller 
> > (this
> > is different from referral-as-final-result). I tweaked the code so 
> > that
> > this would come back as well. I also added some extra debugging at
> > level 9 so we can see when these occur, what they were and that 
> > they
> > were ignored.
> > 
> > As discussed above, I just changed the referral check around
> > sdap_get_generic_ext_recv() to check for ref_count > 0 instead of
> > ERR_REFERRALS. I didn't remove that completely from the system just 
> > in
> > case we decide to use the error for something else involving 
> > referrals
> > in the future.
> > 
> > I retested these patches against the problematic cross-realm GPO 
> > case,
> > which worked successfully.
> 
> 
> > From 20191f9c34336b3920db8d5774593c4562cefdb7 Mon Sep 17 00:00:00 
> > 2001
> > From: Stephen Gallagher <sgall...@redhat.com>
> > Date: Fri, 1 May 2015 11:42:06 -0400
> > Subject: [PATCH 1/2] LDAP: Support returning referral information
> 
> Thank you, this was really useful. I tested the referral patch with
> universal groups and GC support disabled which always triggers 
> referrals
> to the trusted domains. The referrals were ignored as they're 
> supposed
> to, so we're not going to regress there..
> 
> ACK
> 
> > From 9369111f0775cbc4c10b5857046c756ae510033f Mon Sep 17 00:00:00 
> > 2001
> > From: Stephen Gallagher <sgall...@redhat.com>
> > Date: Fri, 1 May 2015 16:26:36 -0400
> > Subject: [PATCH 2/2] AD GPO: Support processing referrals
> > 
> > For GPOs assigned to a site, it's possible that their definition
> > actually exists in another domain. To retrieve this information,
> > we need to follow the referral and perform a base search on
> > another domain controller.
> > 
> > Resolves:
> > https://fedorahosted.org/sssd/ticket/2645
> 
> Some nitpicks are inline, but mostly the patch looks good!
> 
> [...]
> 
> > +
> > +            subreq = ad_gpo_get_sd_referral_send(state, state->ev,
> > +                                                 state
> > ->access_ctx,
> > +                                                 state->opts,
> > +                                                 refs[0],
> > +                                                 state
> > ->host_domain,
> > +                                                 state->timeout);
> 
> Missing NULL check for subreq
> 

Good catch. Fixed.


> > +    /* Request the referred GPO data */
> > +    subreq = sdap_sd_search_send(state, state->ev, state->opts,
> > +                                 sdap_id_op_handle(state->ref_op),
> > +                                 state->gpo_dn,
> > +                                 SECINFO_DACL,
> > +                                 attrs,
> > +                                 state->timeout);
> > +    if (subreq == NULL) {
> > +        DEBUG(SSSDBG_OP_FAILURE, "sdap_sd_search_send failed.\n");
> 
> Copy-n-paste bug; the error handler should read:
>            tevent_req_error(req, ENOMEM);
>            return;
> 
> Interestingly enough, gcc emits a warning here, but Coverity didn't
> catch this bug..
> 

I'm curious what warning GCC saw. In any case, fixed.

> > +        return ENOMEM;
> > +    }
> > +    tevent_req_set_callback(subreq, 
> > ad_gpo_get_sd_referral_search_done, req);
> > +
> > +}
> 
> [...]
> 
> > index 
> > 2ffc2a170c2277aa2ea40e84bb697c62542aa266..760fb3df5148f46bf0e7e0fdb9110456685a914c
> >  
> > 100644
> > --- a/src/providers/ldap/sdap_async.c
> > +++ b/src/providers/ldap/sdap_async.c
> 
> The changes to the sdap_async module look OK to me, but can you split
> them into a separate patch, please?
> 

I considered it, but then decided against splitting it. There really
isn't sufficient value in splitting it. The sdap_async.c portion is
fairly short and clearly only useful in context.


> >  int sdap_sd_search_recv(struct tevent_req *req,
> >                          TALLOC_CTX *mem_ctx,
> >                          size_t *_reply_count,
> > -                        struct sysdb_attrs ***_reply)
> > +                        struct sysdb_attrs ***_reply,
> > +                        size_t *_ref_count,
> > +                        char ***_refs)
> >  {
> >      struct sdap_sd_search_state *state = tevent_req_data(req,
> >                                              struct 
> > sdap_sd_search_state);
> >      TEVENT_REQ_RETURN_ON_ERROR(req);
> >  
> >      *_reply_count = state->sreply.reply_count;
> >      *_reply = talloc_steal(mem_ctx, state->sreply.reply);
> >  
> > +    *_ref_count = state->ref_count;
> > +    *_refs = talloc_steal(mem_ctx, state->refs);
> > +
> 
> Would it make sense to allow NULL ref_count and refs? Like this:
>     if (_ref_count) {
>         *_ref_count = state->ref_count;
>     }
>     if (_refs) {
>         *_refs = talloc_steal(mem_ctx, state->refs);
>     }
> 
> >      return EOK;
> >  }
> >  

I don't see a real need for it, but since it's not harmful, I added it
anyway.



> > f2ea9bf2ecdaeefa897d414df051532c6219cb97..1861b3e7cbf4edb2df7a963b5
> > 28ef18fd21d6f46
> > 100644
> > --- a/src/providers/ldap/sdap_async.h
> > +++ b/src/providers/ldap/sdap_async.h
> > @@ -249,13 +249,25 @@ sdap_sd_search_send(TALLOC_CTX *memctx,
> >                 const char *base_dn,
> >                 int sd_flags,
> >                 const char **attrs,
> >                 int timeout);
> >  int sdap_sd_search_recv(struct tevent_req *req,
> > -                   TALLOC_CTX *mem_ctx,
> > -                   size_t *reply_count,
> > -                   struct sysdb_attrs ***reply);
> > +                        TALLOC_CTX *mem_ctx,
> > +                        size_t *_reply_count,
> > +                        struct sysdb_attrs ***_reply,
> > +                        size_t *_ref_count,
> > +                        char ***_refs);
> > +
> > +struct tevent_req *
> > +sdap_sd_follow_referral_send(TALLOC_CTX *mem_ctx,
> > +                             const char *ref);
> > +
> > +errno_t
> > +sdap_sd_follow_referral_recv(struct tevent_req *req,
> > +                             TALLOC_CTX *mem_ctx,
> > +                             size_t *_reply_count,
> > +                             struct sysdb_attrs ***_reply);
> 
> I only see declarations, not the function bodies or uses?
Whoops. That was part of an aborted change I made. I forgot to remove
the prototypes from the header.



New patches attached. I retested the changes as well. (On my new
Vagrant setup, no less)


From 31af96ccb5e1d49e5457c64db1793fe4d160a101 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgall...@redhat.com>
Date: Fri, 1 May 2015 16:26:36 -0400
Subject: [PATCH 2/2] AD GPO: Support processing referrals

For GPOs assigned to a site, it's possible that their definition
actually exists in another domain. To retrieve this information,
we need to follow the referral and perform a base search on
another domain controller.

Resolves:
https://fedorahosted.org/sssd/ticket/2645
---
 src/providers/ad/ad_gpo.c       | 479 +++++++++++++++++++++++++++++++++++-----
 src/providers/ad/ad_gpo.h       |   7 +
 src/providers/ldap/sdap_async.c |  32 ++-
 src/providers/ldap/sdap_async.h |   8 +-
 4 files changed, 463 insertions(+), 63 deletions(-)

diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c
index 4cfd26800da6c8d77fa4b5ee133a7adab346906c..4e7afb12a73313ece00c6ffdd24cfad1b73a831d 100644
--- a/src/providers/ad/ad_gpo.c
+++ b/src/providers/ad/ad_gpo.c
@@ -144,17 +144,20 @@ struct tevent_req *ad_gpo_process_som_send(TALLOC_CTX *mem_ctx,
                                            const char *domain_name);
 int ad_gpo_process_som_recv(struct tevent_req *req,
                             TALLOC_CTX *mem_ctx,
                             struct gp_som ***som_list);
 
-struct tevent_req *ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
-                                           struct tevent_context *ev,
-                                           struct sdap_id_op *sdap_op,
-                                           struct sdap_options *opts,
-                                           char *server_hostname,
-                                           int timeout,
-                                           struct gp_som **som_list);
+struct tevent_req *
+ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
+                        struct tevent_context *ev,
+                        struct sdap_id_op *sdap_op,
+                        struct sdap_options *opts,
+                        char *server_hostname,
+                        struct sss_domain_info *host_domain,
+                        struct ad_access_ctx *access_ctx,
+                        int timeout,
+                        struct gp_som **som_list);
 int ad_gpo_process_gpo_recv(struct tevent_req *req,
                             TALLOC_CTX *mem_ctx,
                             struct gp_gpo ***candidate_gpos,
                             int *num_candidate_gpos);
 
@@ -1456,10 +1459,11 @@ ad_gpo_perform_hbac_processing(TALLOC_CTX *mem_ctx,
 /* == ad_gpo_access_send/recv implementation ================================*/
 
 struct ad_gpo_access_state {
     struct tevent_context *ev;
     struct ldb_context *ldb_ctx;
+    struct ad_access_ctx *access_ctx;
     enum gpo_access_control_mode gpo_mode;
     enum gpo_map_type gpo_map_type;
     struct sdap_id_conn_ctx *conn;
     struct sdap_id_op *sdap_op;
     char *server_hostname;
@@ -1575,10 +1579,11 @@ ad_gpo_access_send(TALLOC_CTX *mem_ctx,
     state->user = user;
     state->ldb_ctx = sysdb_ctx_get_ldb(state->host_domain->sysdb);
     state->gpo_mode = ctx->gpo_access_control_mode;
     state->gpo_timeout_option = ctx->gpo_cache_timeout;
     state->ad_hostname = dp_opt_get_string(ctx->ad_options, AD_HOSTNAME);
+    state->access_ctx = ctx;
     state->opts = ctx->sdap_access_ctx->id_ctx->opts;
     state->timeout = dp_opt_get_int(state->opts->basic, SDAP_SEARCH_TIMEOUT);
     state->conn = ad_get_dom_ldap_conn(ctx->ad_id_ctx, state->host_domain);
     state->sdap_op = sdap_id_op_create(state, state->conn->conn_cache);
     if (state->sdap_op == NULL) {
@@ -1889,10 +1894,12 @@ ad_gpo_process_som_done(struct tevent_req *subreq)
     subreq = ad_gpo_process_gpo_send(state,
                                      state->ev,
                                      state->sdap_op,
                                      state->opts,
                                      state->server_hostname,
+                                     state->host_domain,
+                                     state->access_ctx,
                                      state->timeout,
                                      som_list);
     if (subreq == NULL) {
         ret = ENOMEM;
         goto done;
@@ -3348,14 +3355,16 @@ static errno_t ad_gpo_parse_sd(TALLOC_CTX *mem_ctx,
 }
 
 /* == ad_gpo_process_gpo_send/recv implementation ========================== */
 
 struct ad_gpo_process_gpo_state {
+    struct ad_access_ctx *access_ctx;
     struct tevent_context *ev;
     struct sdap_id_op *sdap_op;
     struct sdap_options *opts;
     char *server_hostname;
+    struct sss_domain_info *host_domain;
     int timeout;
     struct gp_gpo **candidate_gpos;
     int num_candidate_gpos;
     int gpo_index;
 };
@@ -3376,10 +3385,12 @@ struct tevent_req *
 ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
                         struct tevent_context *ev,
                         struct sdap_id_op *sdap_op,
                         struct sdap_options *opts,
                         char *server_hostname,
+                        struct sss_domain_info *host_domain,
+                        struct ad_access_ctx *access_ctx,
                         int timeout,
                         struct gp_som **som_list)
 {
     struct tevent_req *req;
     struct ad_gpo_process_gpo_state *state;
@@ -3393,10 +3404,12 @@ ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
 
     state->ev = ev;
     state->sdap_op = sdap_op;
     state->opts = opts;
     state->server_hostname = server_hostname;
+    state->host_domain = host_domain;
+    state->access_ctx = access_ctx;
     state->timeout = timeout;
     state->gpo_index = 0;
     state->candidate_gpos = NULL;
     state->num_candidate_gpos = 0;
 
@@ -3435,13 +3448,11 @@ immediately:
 }
 
 static errno_t
 ad_gpo_get_gpo_attrs_step(struct tevent_req *req)
 {
-    const char *attrs[] = {AD_AT_NT_SEC_DESC, AD_AT_CN, AD_AT_FILE_SYS_PATH,
-                           AD_AT_MACHINE_EXT_NAMES, AD_AT_FUNC_VERSION,
-                           AD_AT_FLAGS, NULL};
+    const char *attrs[] = AD_GPO_ATTRS;
     struct tevent_req *subreq;
     struct ad_gpo_process_gpo_state *state;
 
     state = tevent_req_data(req, struct ad_gpo_process_gpo_state);
 
@@ -3455,71 +3466,172 @@ ad_gpo_get_gpo_attrs_step(struct tevent_req *req)
     subreq = sdap_sd_search_send(state, state->ev,
                                  state->opts, sdap_id_op_handle(state->sdap_op),
                                  gpo_dn, SECINFO_DACL, attrs, state->timeout);
 
     if (subreq == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_sd_search_send failed.\n");
         return ENOMEM;
     }
 
     tevent_req_set_callback(subreq, ad_gpo_get_gpo_attrs_done, req);
     return EAGAIN;
 }
 
+static errno_t
+ad_gpo_sd_process_attrs(struct tevent_req *req,
+                        char *smb_host,
+                        struct sysdb_attrs *result);
+void
+ad_gpo_get_sd_referral_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+ad_gpo_get_sd_referral_send(TALLOC_CTX *mem_ctx,
+                            struct tevent_context *ev,
+                            struct ad_access_ctx *access_ctx,
+                            struct sdap_options *opts,
+                            const char *referral,
+                            struct sss_domain_info *host_domain,
+                            int timeout);
+errno_t
+ad_gpo_get_sd_referral_recv(struct tevent_req *req,
+                            TALLOC_CTX *mem_ctx,
+                            char **_smb_host,
+                            struct sysdb_attrs **_reply);
+
 static void
 ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
 {
     struct tevent_req *req;
     struct ad_gpo_process_gpo_state *state;
     int ret;
     int dp_error;
-    size_t num_results;
+    size_t num_results, refcount;
     struct sysdb_attrs **results;
+    char **refs;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct ad_gpo_process_gpo_state);
+
+    ret = sdap_sd_search_recv(subreq, state,
+                              &num_results, &results,
+                              &refcount, &refs);
+    talloc_zfree(subreq);
+
+    if (ret != EOK) {
+        ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
+
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Unable to get GPO attributes: [%d](%s)\n",
+              ret, sss_strerror(ret));
+        ret = ENOENT;
+        goto done;
+    }
+
+    if ((num_results < 1) || (results == NULL)) {
+        if (refcount == 1) {
+            /* If we were redirected to a referral, process it.
+             * There must be a single referral result here; if we get
+             * more than one (or zero) it's a bug.
+             */
+
+            subreq = ad_gpo_get_sd_referral_send(state, state->ev,
+                                                 state->access_ctx,
+                                                 state->opts,
+                                                 refs[0],
+                                                 state->host_domain,
+                                                 state->timeout);
+            if (!subreq) {
+                ret = ENOMEM;
+                goto done;
+            }
+
+            tevent_req_set_callback(subreq, ad_gpo_get_sd_referral_done, req);
+            ret = EAGAIN;
+            goto done;
+
+        } else {
+            const char *gpo_dn = state->candidate_gpos[state->gpo_index]->gpo_dn;
+
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "No attrs found for GPO [%s].", gpo_dn);
+            ret = ENOENT;
+            goto done;
+        }
+    } else if (num_results > 1) {
+        DEBUG(SSSDBG_OP_FAILURE, "Received multiple replies\n");
+        ret = ERR_INTERNAL;
+        goto done;
+    }
+
+    ret = ad_gpo_sd_process_attrs(req, state->server_hostname, results[0]);
+
+done:
+
+   if (ret == EOK) {
+       tevent_req_done(req);
+   } else if (ret != EAGAIN) {
+       tevent_req_error(req, ret);
+   }
+}
+
+void
+ad_gpo_get_sd_referral_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    int dp_error;
+    struct sysdb_attrs *reply;
+    char *smb_host;
+
+    struct tevent_req *req =
+            tevent_req_callback_data(subreq, struct tevent_req);
+    struct ad_gpo_process_gpo_state *state =
+            tevent_req_data(req, struct ad_gpo_process_gpo_state);
+
+    ret = ad_gpo_get_sd_referral_recv(subreq, state, &smb_host, &reply);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        /* Terminate the sdap_id_op */
+        ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
+
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Unable to get referred GPO attributes: [%d](%s)\n",
+              ret, sss_strerror(ret));
+
+        goto done;
+    }
+
+    /* Lookup succeeded. Process it */
+    ret = ad_gpo_sd_process_attrs(req, smb_host, reply);
+
+done:
+
+   if (ret == EOK) {
+       tevent_req_done(req);
+   } else if (ret != EAGAIN) {
+       tevent_req_error(req, ret);
+   }
+}
+
+static errno_t
+ad_gpo_sd_process_attrs(struct tevent_req *req,
+                        char *smb_host,
+                        struct sysdb_attrs *result)
+{
+    struct ad_gpo_process_gpo_state *state;
+    struct gp_gpo *gp_gpo;
+    int ret;
     struct ldb_message_element *el = NULL;
     const char *gpo_guid = NULL;
     const char *raw_file_sys_path = NULL;
     char *file_sys_path = NULL;
     uint8_t *raw_machine_ext_names = NULL;
 
-    req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct ad_gpo_process_gpo_state);
-
-    ret = sdap_sd_search_recv(subreq, state, &num_results, &results);
-    talloc_zfree(subreq);
-
-    if (ret != EOK) {
-        ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
-
-        DEBUG(SSSDBG_OP_FAILURE,
-              "Unable to get GPO attributes: [%d](%s)\n",
-              ret, sss_strerror(ret));
-        ret = ENOENT;
-        goto done;
-    }
-
-    if ((num_results < 1) || (results == NULL)) {
-        const char *gpo_dn = state->candidate_gpos[state->gpo_index]->gpo_dn;
-
-        DEBUG(SSSDBG_OP_FAILURE,
-              "BUG: No attrs found for GPO [%s]. This was likely caused by "
-              "the GPO entry being a referred to another domain controller."
-              " SSSD does not yet support this configuration. See upstream "
-              "ticket #2645 for more information.\n",
-              gpo_dn);
-        ret = ERR_INTERNAL;
-        goto done;
-    }
-    else if (num_results > 1) {
-        DEBUG(SSSDBG_OP_FAILURE, "Received multiple replies\n");
-        ret = ERR_INTERNAL;
-        goto done;
-    }
-
-    struct gp_gpo *gp_gpo = state->candidate_gpos[state->gpo_index];
+    gp_gpo = state->candidate_gpos[state->gpo_index];
 
     /* retrieve AD_AT_CN */
-    ret = sysdb_attrs_get_string(results[0], AD_AT_CN, &gpo_guid);
+    ret = sysdb_attrs_get_string(result, AD_AT_CN, &gpo_guid);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
               "sysdb_attrs_get_string failed: [%d](%s)\n",
               ret, sss_strerror(ret));
         goto done;
@@ -3533,11 +3645,11 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
 
     DEBUG(SSSDBG_TRACE_ALL, "populating attrs for gpo_guid: %s\n",
           gp_gpo->gpo_guid);
 
     /* retrieve AD_AT_FILE_SYS_PATH */
-    ret = sysdb_attrs_get_string(results[0],
+    ret = sysdb_attrs_get_string(result,
                                  AD_AT_FILE_SYS_PATH,
                                  &raw_file_sys_path);
 
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
@@ -3546,11 +3658,11 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
         goto done;
     }
 
     file_sys_path = talloc_strdup(gp_gpo, raw_file_sys_path);
 
-    ret = ad_gpo_extract_smb_components(gp_gpo, state->server_hostname,
+    ret = ad_gpo_extract_smb_components(gp_gpo, smb_host,
                                         file_sys_path, &gp_gpo->smb_server,
                                         &gp_gpo->smb_share, &gp_gpo->smb_path);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
               "unable to extract smb components from file_sys_path: [%d](%s)\n",
@@ -3561,11 +3673,11 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
     DEBUG(SSSDBG_TRACE_ALL, "smb_server: %s\n", gp_gpo->smb_server);
     DEBUG(SSSDBG_TRACE_ALL, "smb_share: %s\n", gp_gpo->smb_share);
     DEBUG(SSSDBG_TRACE_ALL, "smb_path: %s\n", gp_gpo->smb_path);
 
     /* retrieve AD_AT_FUNC_VERSION */
-    ret = sysdb_attrs_get_int32_t(results[0], AD_AT_FUNC_VERSION,
+    ret = sysdb_attrs_get_int32_t(result, AD_AT_FUNC_VERSION,
                                   &gp_gpo->gpo_func_version);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
               "sysdb_attrs_get_int32_t failed: [%d](%s)\n",
               ret, sss_strerror(ret));
@@ -3574,11 +3686,11 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
 
     DEBUG(SSSDBG_TRACE_ALL, "gpo_func_version: %d\n",
                             gp_gpo->gpo_func_version);
 
     /* retrieve AD_AT_FLAGS */
-    ret = sysdb_attrs_get_int32_t(results[0], AD_AT_FLAGS,
+    ret = sysdb_attrs_get_int32_t(result, AD_AT_FLAGS,
                                   &gp_gpo->gpo_flags);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
               "sysdb_attrs_get_int32_t failed: [%d](%s)\n",
               ret, sss_strerror(ret));
@@ -3586,11 +3698,11 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
     }
 
     DEBUG(SSSDBG_TRACE_ALL, "gpo_flags: %d\n", gp_gpo->gpo_flags);
 
     /* retrieve AD_AT_NT_SEC_DESC */
-    ret = sysdb_attrs_get_el(results[0], AD_AT_NT_SEC_DESC, &el);
+    ret = sysdb_attrs_get_el(result, AD_AT_NT_SEC_DESC, &el);
     if (ret != EOK && ret != ENOENT) {
         DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el() failed\n");
         goto done;
     }
     if ((ret == ENOENT) || (el->num_values == 0)) {
@@ -3606,11 +3718,11 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
         DEBUG(SSSDBG_OP_FAILURE, "ad_gpo_parse_sd() failed\n");
         goto done;
     }
 
     /* retrieve AD_AT_MACHINE_EXT_NAMES */
-    ret = sysdb_attrs_get_el(results[0], AD_AT_MACHINE_EXT_NAMES, &el);
+    ret = sysdb_attrs_get_el(result, AD_AT_MACHINE_EXT_NAMES, &el);
     if (ret != EOK && ret != ENOENT) {
         DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el() failed\n");
         goto done;
     }
 
@@ -3640,15 +3752,11 @@ ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
 
     ret = ad_gpo_get_gpo_attrs_step(req);
 
  done:
 
-    if (ret == EOK) {
-        tevent_req_done(req);
-    } else if (ret != EAGAIN) {
-        tevent_req_error(req, ret);
-    }
+    return ret;
 }
 
 int
 ad_gpo_process_gpo_recv(struct tevent_req *req,
                         TALLOC_CTX *mem_ctx,
@@ -4017,5 +4125,260 @@ gpo_fork_child(struct tevent_req *req)
         return err;
     }
 
     return EOK;
 }
+
+struct ad_gpo_get_sd_referral_state {
+    struct tevent_context *ev;
+    struct ad_access_ctx *access_ctx;
+    struct sdap_options *opts;
+    struct sss_domain_info *host_domain;
+    struct sss_domain_info *ref_domain;
+    struct sdap_id_conn_ctx *conn;
+    struct sdap_id_op *ref_op;
+    int timeout;
+    char *gpo_dn;
+    char *smb_host;
+
+
+    struct sysdb_attrs *reply;
+};
+
+static void
+ad_gpo_get_sd_referral_conn_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+ad_gpo_get_sd_referral_send(TALLOC_CTX *mem_ctx,
+                            struct tevent_context *ev,
+                            struct ad_access_ctx *access_ctx,
+                            struct sdap_options *opts,
+                            const char *referral,
+                            struct sss_domain_info *host_domain,
+                            int timeout)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct ad_gpo_get_sd_referral_state *state;
+    struct tevent_req *subreq;
+    LDAPURLDesc *lud;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct ad_gpo_get_sd_referral_state);
+    if (!req) return NULL;
+
+    state->ev = ev;
+    state->access_ctx = access_ctx;
+    state->opts = opts;
+    state->host_domain = host_domain;
+    state->timeout = timeout;
+
+    /* Parse the URL for the domain */
+    ret = ldap_url_parse(referral, &lud);
+    if (ret != LDAP_SUCCESS) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed to parse referral URI (%s)!\n", referral);
+        ret = EINVAL;
+        goto done;
+    }
+
+    state->gpo_dn = talloc_strdup(state, lud->lud_dn);
+    if (!state->gpo_dn) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Could not copy referral DN (%s)!\n", lud->lud_dn);
+        ldap_free_urldesc(lud);
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Active Directory returns the domain name as the hostname
+     * in these referrals, so we can use that to look up the
+     * necessary connection.
+     */
+    state->ref_domain = find_domain_by_name(state->host_domain,
+                                            lud->lud_host, true);
+    ldap_free_urldesc(lud);
+    if (!state->ref_domain) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Could not find domain matching [%s]\n",
+              lud->lud_host);
+        ret = EIO;
+        goto done;
+    }
+
+    state->conn = ad_get_dom_ldap_conn(state->access_ctx->ad_id_ctx,
+                                       state->ref_domain);
+    if (!state->conn) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "No connection for %s\n", state->ref_domain->name);
+        ret = EINVAL;
+        goto done;
+    }
+
+    /* Get the hostname we're going to connect to.
+     * We'll need this later for performing the samba
+     * connection.
+     */
+    ret = ldap_url_parse(state->conn->service->uri, &lud);
+    if (ret != LDAP_SUCCESS) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed to parse service URI (%s)!\n", referral);
+        ret = EINVAL;
+        goto done;
+    }
+
+    state->smb_host = talloc_strdup(state, lud->lud_host);
+    ldap_free_urldesc(lud);
+    if (!state->smb_host) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Start an ID operation for the referral */
+    state->ref_op = sdap_id_op_create(state, state->conn->conn_cache);
+    if (!state->ref_op) {
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed.\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Establish the sdap_id_op connection */
+    subreq = sdap_id_op_connect_send(state->ref_op, state, &ret);
+    if (subreq == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_connect_send failed: %d(%s).\n",
+                                  ret, sss_strerror(ret));
+        goto done;
+    }
+    tevent_req_set_callback(subreq, ad_gpo_get_sd_referral_conn_done, req);
+
+done:
+
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, ev);
+    }
+    return req;
+}
+
+static void
+ad_gpo_get_sd_referral_search_done(struct tevent_req *subreq);
+
+static void
+ad_gpo_get_sd_referral_conn_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    int dp_error;
+    const char *attrs[] = AD_GPO_ATTRS;
+
+    struct tevent_req *req =
+            tevent_req_callback_data(subreq, struct tevent_req);
+    struct ad_gpo_get_sd_referral_state *state =
+            tevent_req_data(req, struct ad_gpo_get_sd_referral_state);
+
+    ret = sdap_id_op_connect_recv(subreq, &dp_error);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        if (dp_error == DP_ERR_OFFLINE) {
+            DEBUG(SSSDBG_TRACE_FUNC,
+                  "Backend is marked offline, retry later!\n");
+            tevent_req_done(req);
+        } else {
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  "Cross-realm GPO processing failed to connect to " \
+                   "referred LDAP server: (%d)[%s]\n",
+                   ret, sss_strerror(ret));
+            tevent_req_error(req, ret);
+        }
+        return;
+    }
+
+    /* Request the referred GPO data */
+    subreq = sdap_sd_search_send(state, state->ev, state->opts,
+                                 sdap_id_op_handle(state->ref_op),
+                                 state->gpo_dn,
+                                 SECINFO_DACL,
+                                 attrs,
+                                 state->timeout);
+    if (subreq == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_sd_search_send failed.\n");
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+    tevent_req_set_callback(subreq, ad_gpo_get_sd_referral_search_done, req);
+
+}
+
+static void
+ad_gpo_get_sd_referral_search_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    int dp_error;
+    size_t num_results, num_refs;
+    struct sysdb_attrs **results = NULL;
+    char **refs;
+    struct tevent_req *req =
+            tevent_req_callback_data(subreq, struct tevent_req);
+    struct ad_gpo_get_sd_referral_state *state =
+            tevent_req_data(req, struct ad_gpo_get_sd_referral_state);
+
+    ret = sdap_sd_search_recv(subreq, NULL,
+                              &num_results, &results,
+                              &num_refs, &refs);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        ret = sdap_id_op_done(state->ref_op, ret, &dp_error);
+
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Unable to get GPO attributes: [%d](%s)\n",
+              ret, sss_strerror(ret));
+        ret = ENOENT;
+        goto done;
+
+    }
+
+    if ((num_results < 1) || (results == NULL)) {
+        /* TODO:
+         * It's strictly possible for the referral search to return
+         * another referral value here, but it shouldn't actually
+         * happen with Active Directory. Properly handling (and
+         * limiting) the referral chain would be fairly complex, so
+         * we will do it later if it ever becomes necessary.
+         */
+        DEBUG(SSSDBG_OP_FAILURE,
+              "No attrs found for referred GPO [%s].", state->gpo_dn);
+        ret = ENOENT;
+        goto done;
+
+    } else if (num_results > 1) {
+        DEBUG(SSSDBG_OP_FAILURE, "Received multiple replies\n");
+        ret = ERR_INTERNAL;
+        goto done;
+    }
+
+    state->reply = talloc_steal(state, results[0]);
+
+done:
+   talloc_free(results);
+
+   if (ret == EOK) {
+       tevent_req_done(req);
+   } else if (ret != EAGAIN) {
+       tevent_req_error(req, ret);
+   }
+}
+
+errno_t
+ad_gpo_get_sd_referral_recv(struct tevent_req *req,
+                            TALLOC_CTX *mem_ctx,
+                            char **_smb_host,
+                            struct sysdb_attrs **_reply)
+{
+    struct ad_gpo_get_sd_referral_state *state =
+                tevent_req_data(req, struct ad_gpo_get_sd_referral_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *_smb_host = talloc_steal(mem_ctx, state->smb_host);
+    *_reply = talloc_steal(mem_ctx, state->reply);
+
+    return EOK;
+}
diff --git a/src/providers/ad/ad_gpo.h b/src/providers/ad/ad_gpo.h
index 9fd590a2b262b66c1efd493d8736774bdfa61b40..f57889fca1c7fa77f25c345c1c969d6b499217e5 100644
--- a/src/providers/ad/ad_gpo.h
+++ b/src/providers/ad/ad_gpo.h
@@ -25,10 +25,17 @@
 
 #include "providers/ad/ad_access.h"
 
 #define AD_GPO_CHILD_OUT_FILENO 3
 
+#define AD_GPO_ATTRS {AD_AT_NT_SEC_DESC, \
+                      AD_AT_CN, AD_AT_FILE_SYS_PATH, \
+                      AD_AT_MACHINE_EXT_NAMES, \
+                      AD_AT_FUNC_VERSION, \
+                      AD_AT_FLAGS, \
+                      NULL}
+
 /*
  * This pair of functions provides client-side GPO processing.
  *
  * While a GPO can target both user and computer objects, this
  * implementation only supports targetting of computer objects.
diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
index 2ffc2a170c2277aa2ea40e84bb697c62542aa266..ca70976b184b8b36fe0244a54f976da86a7b00f0 100644
--- a/src/providers/ldap/sdap_async.c
+++ b/src/providers/ldap/sdap_async.c
@@ -2004,10 +2004,14 @@ struct sdap_sd_search_state {
   LDAPControl **ctrls;
   struct sdap_options *opts;
   size_t reply_count;
   struct sysdb_attrs **reply;
   struct sdap_reply sreply;
+
+  /* Referrals returned by the search */
+  size_t ref_count;
+  char **refs;
 };
 
 static int sdap_sd_search_create_control(struct sdap_handle *sh,
 					 int val,
 					 LDAPControl **ctrl);
@@ -2135,16 +2139,30 @@ static errno_t sdap_sd_search_parse_entry(struct sdap_handle *sh,
     return EOK;
 }
 
 static void sdap_sd_search_done(struct tevent_req *subreq)
 {
+    int ret;
+
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
     struct sdap_sd_search_state *state =
                 tevent_req_data(req, struct sdap_sd_search_state);
 
-    return generic_ext_search_handler(subreq, state->opts);
+    ret = sdap_get_generic_ext_recv(subreq, state,
+                                    &state->ref_count,
+                                    &state->refs);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "sdap_get_generic_ext_recv failed [%d]: %s\n",
+              ret, sss_strerror(ret));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
 }
 
 static int sdap_sd_search_ctrls_destructor(void *ptr)
 {
     LDAPControl **ctrls = talloc_get_type(ptr, LDAPControl *);;
@@ -2156,19 +2174,29 @@ static int sdap_sd_search_ctrls_destructor(void *ptr)
 }
 
 int sdap_sd_search_recv(struct tevent_req *req,
                         TALLOC_CTX *mem_ctx,
                         size_t *_reply_count,
-                        struct sysdb_attrs ***_reply)
+                        struct sysdb_attrs ***_reply,
+                        size_t *_ref_count,
+                        char ***_refs)
 {
     struct sdap_sd_search_state *state = tevent_req_data(req,
                                             struct sdap_sd_search_state);
     TEVENT_REQ_RETURN_ON_ERROR(req);
 
     *_reply_count = state->sreply.reply_count;
     *_reply = talloc_steal(mem_ctx, state->sreply.reply);
 
+    if(_ref_count) {
+        *_ref_count = state->ref_count;
+    }
+
+    if (_refs) {
+        *_refs = talloc_steal(mem_ctx, state->refs);
+    }
+
     return EOK;
 }
 
 /* ==Attribute scoped search============================================ */
 struct sdap_asq_search_state {
diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
index f2ea9bf2ecdaeefa897d414df051532c6219cb97..b5db64d7fa3c852fba60e07db19e823e818d29f3 100644
--- a/src/providers/ldap/sdap_async.h
+++ b/src/providers/ldap/sdap_async.h
@@ -249,13 +249,15 @@ sdap_sd_search_send(TALLOC_CTX *memctx,
 		    const char *base_dn,
 		    int sd_flags,
 		    const char **attrs,
 		    int timeout);
 int sdap_sd_search_recv(struct tevent_req *req,
-			TALLOC_CTX *mem_ctx,
-			size_t *reply_count,
-			struct sysdb_attrs ***reply);
+                        TALLOC_CTX *mem_ctx,
+                        size_t *_reply_count,
+                        struct sysdb_attrs ***_reply,
+                        size_t *_ref_count,
+                        char ***_refs);
 
 errno_t
 sdap_attrs_add_ldap_attr(struct sysdb_attrs *ldap_attrs,
                          const char *attr_name,
                          const char *attr_desc,
-- 
2.4.1

From 21bd33a95d2b9b9fc08196f91df0f08abd3bd480 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgall...@redhat.com>
Date: Fri, 1 May 2015 11:42:06 -0400
Subject: [PATCH 1/2] LDAP: Support returning referral information

Some callers may be interested in the raw referral values returned from
a lookup. This patch allows interested consumers to get these referrals
back and process them if they wish. It does not implement a generic
automatic following of referrals.
---
 src/providers/ldap/sdap_async.c | 134 +++++++++++++++++++++++++++++++++-------
 1 file changed, 112 insertions(+), 22 deletions(-)

diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
index 0d1f0195c0b96d44151487642d30b631063d617a..2ffc2a170c2277aa2ea40e84bb697c62542aa266 100644
--- a/src/providers/ldap/sdap_async.c
+++ b/src/providers/ldap/sdap_async.c
@@ -297,19 +297,14 @@ static void sdap_process_message(struct tevent_context *ev,
     DEBUG(SSSDBG_TRACE_ALL,
           "Message type: [%s]\n", sdap_ldap_result_str(msgtype));
 
     switch (msgtype) {
     case LDAP_RES_SEARCH_ENTRY:
+    case LDAP_RES_SEARCH_REFERENCE:
         /* go and process entry */
         break;
 
-    case LDAP_RES_SEARCH_REFERENCE:
-        /* more ops to come with this msgid */
-        /* just ignore */
-        ldap_msgfree(msg);
-        return;
-
     case LDAP_RES_BIND:
     case LDAP_RES_SEARCH_RESULT:
     case LDAP_RES_MODIFY:
     case LDAP_RES_ADD:
     case LDAP_RES_DELETE:
@@ -1154,10 +1149,13 @@ struct sdap_get_generic_ext_state {
 
     LDAPControl **serverctrls;
     int nserverctrls;
     LDAPControl **clientctrls;
 
+    size_t ref_count;
+    char **refs;
+
     sdap_parse_cb parse_cb;
     void *cb_data;
 
     bool allow_paging;
 };
@@ -1375,19 +1373,59 @@ static errno_t sdap_get_generic_ext_step(struct tevent_req *req)
 
 done:
     return ret;
 }
 
+static errno_t
+sdap_get_generic_ext_add_references(struct sdap_get_generic_ext_state *state,
+                                    char **refs)
+{
+    int i;
+
+    if (refs == NULL) {
+        /* Rare, but it's possible that we might get a reference result with
+         * no references attached.
+         */
+        return EOK;
+    }
+
+    for (i = 0; refs[i]; i++) {
+        DEBUG(SSSDBG_TRACE_LIBS, "Additional References: %s\n", refs[i]);
+    }
+
+    /* Extend the size of the ref array */
+    state->refs = talloc_realloc(state, state->refs, char *,
+                                 state->ref_count + i);
+    if (state->refs == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "talloc_realloc failed extending ref_array.\n");
+        return ENOMEM;
+    }
+
+    /* Copy in all the references */
+    for (i = 0; refs[i]; i++) {
+        state->refs[state->ref_count + i] =
+                talloc_strdup(state->refs, refs[i]);
+
+        if (state->refs[state->ref_count + i] == NULL) {
+            return ENOMEM;
+        }
+    }
+
+    state->ref_count += i;
+
+    return EOK;
+}
+
 static void sdap_get_generic_op_finished(struct sdap_op *op,
                                          struct sdap_msg *reply,
                                          int error, void *pvt)
 {
     struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
     struct sdap_get_generic_ext_state *state = tevent_req_data(req,
                                             struct sdap_get_generic_ext_state);
     char *errmsg = NULL;
-    int i;
     char **refs = NULL;
     int result;
     int ret;
     int lret;
     ber_int_t total_count;
@@ -1400,12 +1438,31 @@ static void sdap_get_generic_op_finished(struct sdap_op *op,
         return;
     }
 
     switch (ldap_msgtype(reply->msg)) {
     case LDAP_RES_SEARCH_REFERENCE:
-        /* ignore references for now */
-        talloc_free(reply);
+        ret = ldap_parse_reference(state->sh->ldap, reply->msg,
+                                   &refs, NULL, 0);
+        if (ret != LDAP_SUCCESS) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "ldap_parse_reference failed (%d)\n", state->op->msgid);
+            tevent_req_error(req, EIO);
+            return;
+        }
+
+        ret = sdap_get_generic_ext_add_references(state, refs);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "sdap_get_generic_ext_add_references failed: %s(%d)",
+                  sss_strerror(ret), ret);
+            ldap_memvfree((void **)refs);
+            tevent_req_error(req, ret);
+            return;
+        }
+
+        /* Remove the original strings */
+        ldap_memvfree((void **)refs);
 
         /* unlock the operation so that we can proceed with the next result */
         sdap_unlock_next_reply(state->op);
         break;
 
@@ -1454,19 +1511,18 @@ static void sdap_get_generic_op_finished(struct sdap_op *op,
         } else if (result == LDAP_UNAVAILABLE_CRITICAL_EXTENSION) {
             ldap_memfree(errmsg);
             tevent_req_error(req, ENOTSUP);
             return;
         } else if (result == LDAP_REFERRAL) {
-            if (refs != NULL) {
-                for (i = 0; refs[i]; i++) {
-                    DEBUG(SSSDBG_TRACE_LIBS, "Ref: %s\n", refs[i]);
-                }
-                ldap_memvfree((void **) refs);
+            ret = sdap_get_generic_ext_add_references(state, refs);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE,
+                      "sdap_get_generic_ext_add_references failed: %s(%d)",
+                      sss_strerror(ret), ret);
+                tevent_req_error(req, ret);
             }
-            ldap_memfree(errmsg);
-            tevent_req_error(req, ERR_REFERRAL);
-            return;
+            /* For referrals, we need to fall through as if it was LDAP_SUCCESS */
         } else if (result != LDAP_SUCCESS && result != LDAP_NO_SUCH_OBJECT) {
             DEBUG(SSSDBG_OP_FAILURE,
                   "Unexpected result from ldap: %s(%d), %s\n",
                    sss_ldap_err2string(result), result,
                    errmsg ? errmsg : "no errmsg set");
@@ -1532,30 +1588,63 @@ static void sdap_get_generic_op_finished(struct sdap_op *op,
         return;
     }
 }
 
 static int
-sdap_get_generic_ext_recv(struct tevent_req *req)
+sdap_get_generic_ext_recv(struct tevent_req *req,
+                          TALLOC_CTX *mem_ctx,
+                          size_t *ref_count,
+                          char ***refs)
 {
+    struct sdap_get_generic_ext_state *state =
+            tevent_req_data(req, struct sdap_get_generic_ext_state);
+
     TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    if (ref_count) {
+        *ref_count = state->ref_count;
+    }
+
+    if (refs) {
+        *refs = talloc_steal(mem_ctx, state->refs);
+    }
+
     return EOK;
 }
 
+/* This search handler can be used by most calls */
 static void generic_ext_search_handler(struct tevent_req *subreq,
                                        struct sdap_options *opts)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
     int ret;
+    size_t ref_count, i;
+    char **refs;
 
-    ret = sdap_get_generic_ext_recv(subreq);
+    ret = sdap_get_generic_ext_recv(subreq, req, &ref_count, &refs);
     talloc_zfree(subreq);
-    if (ret == ERR_REFERRAL) {
+    if (ref_count > 0) {
         if (dp_opt_get_bool(opts->basic, SDAP_REFERRALS)) {
-            tevent_req_error(req, ret);
+            /* We got back referrals here, but they should have
+             * been processed internally by openldap libs.
+             * This should never happen.
+             */
+            talloc_free(refs);
+            tevent_req_error(req, EINVAL);
             return;
         }
+
+        /* We will ignore referrals in the generic handler */
+        DEBUG(SSSDBG_TRACE_ALL,
+              "Request included referrals which were ignored.\n");
+        if (debug_level & SSSDBG_TRACE_ALL) {
+            for(i = 0; i < ref_count; i++) {
+                DEBUG(SSSDBG_TRACE_ALL,
+                      "    Ref: %s\n", refs[i]);
+            }
+        }
     } else if (ret) {
         DEBUG(SSSDBG_OP_FAILURE,
               "sdap_get_generic_ext_recv failed [%d]: %s\n",
               ret, sss_strerror(ret));
         tevent_req_error(req, ret);
@@ -1563,10 +1652,11 @@ static void generic_ext_search_handler(struct tevent_req *subreq,
     }
 
     tevent_req_done(req);
 }
 
+
 /* ==Generic Search============================================ */
 struct sdap_get_generic_state {
     struct sdap_attr_map *map;
     int map_num_attrs;
 
@@ -2483,11 +2573,11 @@ static void sdap_posix_check_done(struct tevent_req *subreq)
                                                       struct tevent_req);
     struct sdap_posix_check_state *state =
         tevent_req_data(req, struct sdap_posix_check_state);
     errno_t ret;
 
-    ret = sdap_get_generic_ext_recv(subreq);
+    ret = sdap_get_generic_ext_recv(subreq, NULL, NULL, NULL);
     talloc_zfree(subreq);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
               "sdap_get_generic_ext_recv failed [%d]: %s\n",
               ret, strerror(ret));
-- 
2.4.1

Attachment: signature.asc
Description: This is a digitally signed message part

_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org
https://lists.fedorahosted.org/mailman/listinfo/sssd-devel

Reply via email to