s4:winbind Give more detail on the parameters when reporting idmap failure
[ira/wip.git] / source4 / winbind / wb_async_helpers.c
index 89286bccf17c09848e5699d8197e71191165b6b5..5d530ca149ba8631da7e29edebd770af361e8d5c 100644 (file)
@@ -5,7 +5,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 /*
   a composite API for finding a DC and its name
 */
 
 #include "includes.h"
+#include <tevent.h>
 #include "libcli/composite/composite.h"
 #include "winbind/wb_async_helpers.h"
 
-#include "librpc/gen_ndr/nbt.h"
-#include "librpc/gen_ndr/samr.h"
-#include "lib/messaging/irpc.h"
-#include "librpc/gen_ndr/irpc.h"
-#include "librpc/gen_ndr/ndr_irpc.h"
-#include "libcli/raw/libcliraw.h"
-#include "librpc/gen_ndr/ndr_netlogon.h"
-#include "libcli/auth/credentials.h"
-
-struct finddcs_state {
-       struct wb_finddcs *io;
-       struct composite_context *creq;
-
-       struct nbtd_getdcname *r;
-       struct irpc_request *ireq;
-};
-
-static void finddcs_getdc(struct irpc_request *ireq)
-{
-       struct composite_context *c = talloc_get_type(ireq->async.private,
-                                                     struct composite_context);
-       struct finddcs_state *state = talloc_get_type(c->private_data,
-                                                     struct finddcs_state);
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
 
-       c->status = irpc_call_recv(ireq);
-       if (!NT_STATUS_IS_OK(c->status)) {
-               goto done;
-       }
 
-       state->io->out.dcs[0].name = talloc_steal(state->io->out.dcs,
-                                                 state->r->out.dcname);
+struct lsa_lookupsids_state {
+       struct composite_context *ctx;
+       uint32_t num_sids;
+       struct lsa_LookupSids r;
+       struct lsa_SidArray sids;
+       struct lsa_TransNameArray names;
+       struct lsa_RefDomainList *domains;
+       uint32_t count;
+       struct wb_sid_object **result;
+};
 
-       c->status = NT_STATUS_OK;
-       c->state = COMPOSITE_STATE_DONE;
+static void lsa_lookupsids_recv_names(struct tevent_req *subreq);
 
- done:
-       if (!NT_STATUS_IS_OK(c->status)) {
-               c->state = COMPOSITE_STATE_ERROR;
-       }
-               
-       if (c->state >= COMPOSITE_STATE_DONE &&
-           c->async.fn) {
-               c->async.fn(c);
-       }
-       talloc_free(ireq);
-}
-
-/*
-  called when name resolution is finished
-*/
-static void finddcs_resolve(struct composite_context *res_ctx)
+struct composite_context *wb_lsa_lookupsids_send(TALLOC_CTX *mem_ctx,
+                                                struct dcerpc_pipe *lsa_pipe,
+                                                struct policy_handle *handle,
+                                                uint32_t num_sids,
+                                                const struct dom_sid **sids)
 {
-       struct composite_context *c = talloc_get_type(res_ctx->async.private_data,
-                                                     struct composite_context);
-       struct finddcs_state *state = talloc_get_type(c->private_data,
-                                                     struct finddcs_state);
-       uint32_t *nbt_servers;
-
-       state->io->out.num_dcs = 1;
-       state->io->out.dcs = talloc_array(state, struct nbt_dc_name,
-                                         state->io->out.num_dcs);
-       if (state->io->out.dcs == NULL) {
-               c->status = NT_STATUS_NO_MEMORY;
-               goto done;
-       }
+       struct composite_context *result;
+       struct lsa_lookupsids_state *state;
+       uint32_t i;
+       struct tevent_req *subreq;
 
-       c->status = resolve_name_recv(res_ctx, state->io->out.dcs,
-                                     &state->io->out.dcs[0].address);
-       if (!NT_STATUS_IS_OK(c->status)) {
-               goto done;
-       }
+       result = composite_create(mem_ctx, lsa_pipe->conn->event_ctx);
+       if (result == NULL) goto failed;
 
-       nbt_servers = irpc_servers_byname(state->io->in.msg_ctx, "nbt_server");
-       if ((nbt_servers == NULL) || (nbt_servers[0] == 0)) {
-               c->status = NT_STATUS_NO_LOGON_SERVERS;
-               goto done;
-       }
+       state = talloc(result, struct lsa_lookupsids_state);
+       if (state == NULL) goto failed;
+       result->private_data = state;
+       state->ctx = result;
 
-       state->r = talloc(state, struct nbtd_getdcname);
-       if (state->r == NULL) {
-               c->status = NT_STATUS_NO_MEMORY;
-               goto done;
-       }
+       state->sids.num_sids = num_sids;
+       state->sids.sids = talloc_array(state, struct lsa_SidPtr, num_sids);
+       if (state->sids.sids == NULL) goto failed;
 
-       state->r->in.domainname = talloc_strdup(state->r, lp_workgroup());
-       state->r->in.ip_address = state->io->out.dcs[0].address;
-       state->r->in.my_computername = lp_netbios_name();
-       state->r->in.my_accountname = talloc_asprintf(state->r, "%s$",
-                                                     lp_netbios_name());
-       state->r->in.account_control = ACB_WSTRUST;
-       state->r->in.domain_sid = secrets_get_domain_sid(state->r,
-                                                        lp_workgroup());
-
-       if ((state->r->in.domainname == NULL) ||
-           (state->r->in.my_accountname == NULL)) {
-               DEBUG(0, ("talloc failed\n"));
-               c->status = NT_STATUS_NO_MEMORY;
-               goto done;
-       }
-       if (state->r->in.domain_sid == NULL) {
-               c->status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
-               goto done;
+       for (i=0; i<num_sids; i++) {
+               state->sids.sids[i].sid = dom_sid_dup(state->sids.sids,
+                                                     sids[i]);
+               if (state->sids.sids[i].sid == NULL) goto failed;
        }
 
-       state->ireq = irpc_call_send(state->io->in.msg_ctx, nbt_servers[0],
-                                    &dcerpc_table_irpc, DCERPC_NBTD_GETDCNAME,
-                                    state->r, state);
-       
-       if (state->ireq == NULL) {
-               c->status = NT_STATUS_NO_MEMORY;
-               goto done;
-       }
+       state->domains = talloc(state, struct lsa_RefDomainList);
+       if (state->domains == NULL) goto failed;
+
+       state->count = 0;
+       state->num_sids = num_sids;
+       state->names.count = 0;
+       state->names.names = NULL;
+
+       state->r.in.handle = handle;
+       state->r.in.sids = &state->sids;
+       state->r.in.names = &state->names;
+       state->r.in.level = 1;
+       state->r.in.count = &state->count;
+       state->r.out.names = &state->names;
+       state->r.out.count = &state->count;
+       state->r.out.domains = &state->domains;
+
+       subreq = dcerpc_lsa_LookupSids_r_send(state,
+                                             result->event_ctx,
+                                             lsa_pipe->binding_handle,
+                                             &state->r);
+       if (subreq == NULL) goto failed;
+       tevent_req_set_callback(subreq, lsa_lookupsids_recv_names, state);
 
-       c->status = NT_STATUS_OK;
-       state->ireq->async.fn = finddcs_getdc;
-       state->ireq->async.private = c;
+       return result;
 
- done:
-       if (!NT_STATUS_IS_OK(c->status)) {
-               c->state = COMPOSITE_STATE_ERROR;
-       }
-               
-       if (c->state >= COMPOSITE_STATE_DONE &&
-           c->async.fn) {
-               c->async.fn(c);
-       }
+ failed:
+       talloc_free(result);
+       return NULL;
 }
 
-struct composite_context *wb_finddcs_send(struct wb_finddcs *io,
-                                         struct event_context *event_ctx)
+static void lsa_lookupsids_recv_names(struct tevent_req *subreq)
 {
-       struct composite_context *c;
-       struct finddcs_state *state;
-       struct nbt_name name;
-
-       c = talloc_zero(NULL, struct composite_context);
-       if (c == NULL) goto failed;
-       c->state = COMPOSITE_STATE_IN_PROGRESS;
-       c->event_ctx = event_ctx;
-
-       state = talloc(c, struct finddcs_state);
-       if (state == NULL) goto failed;
-       state->io = io;
-
-       make_nbt_name(&name, io->in.domain, 0x1c);
-       state->creq = resolve_name_send(&name, c->event_ctx,
-                                       lp_name_resolve_order());
+       struct lsa_lookupsids_state *state =
+               tevent_req_callback_data(subreq,
+               struct lsa_lookupsids_state);
+       uint32_t i;
+
+       state->ctx->status = dcerpc_lsa_LookupSids_r_recv(subreq, state);
+       TALLOC_FREE(subreq);
+       if (!composite_is_ok(state->ctx)) return;
+       state->ctx->status = state->r.out.result;
+       if (!NT_STATUS_IS_OK(state->ctx->status) &&
+           !NT_STATUS_EQUAL(state->ctx->status, STATUS_SOME_UNMAPPED)) {
+               composite_error(state->ctx, state->ctx->status);
+               return;
+       }
 
-       if (state->creq == NULL) goto failed;
-       state->creq->async.private_data = c;
-       state->creq->async.fn = finddcs_resolve;
-       c->private_data = state;
+       state->result = talloc_array(state, struct wb_sid_object *,
+                                    state->num_sids);
+       if (composite_nomem(state->result, state->ctx)) return;
+
+       for (i=0; i<state->num_sids; i++) {
+               struct lsa_TranslatedName *name =
+                       &state->r.out.names->names[i];
+               struct lsa_DomainInfo *dom;
+               struct lsa_RefDomainList *domains =
+                       state->domains;
+
+               state->result[i] = talloc_zero(state->result,
+                                              struct wb_sid_object);
+               if (composite_nomem(state->result[i], state->ctx)) return;
+
+               state->result[i]->type = name->sid_type;
+               if (state->result[i]->type == SID_NAME_UNKNOWN) {
+                       continue;
+               }
+
+               if (name->sid_index >= domains->count) {
+                       composite_error(state->ctx,
+                                       NT_STATUS_INVALID_PARAMETER);
+                       return;
+               }
+
+               dom = &domains->domains[name->sid_index];
+               state->result[i]->domain = talloc_reference(state->result[i],
+                                                           dom->name.string);
+               if ((name->sid_type == SID_NAME_DOMAIN) ||
+                   (name->name.string == NULL)) {
+                       state->result[i]->name =
+                               talloc_strdup(state->result[i], "");
+               } else {
+                       state->result[i]->name =
+                               talloc_steal(state->result[i],
+                                            name->name.string);
+               }
+
+               if (composite_nomem(state->result[i]->name, state->ctx)) {
+                       return;
+               }
+       }
 
-       return c;
-failed:
-       talloc_free(c);
-       return NULL;
+       composite_done(state->ctx);
 }
 
-NTSTATUS wb_finddcs_recv(struct composite_context *c, TALLOC_CTX *mem_ctx)
+NTSTATUS wb_lsa_lookupsids_recv(struct composite_context *c,
+                               TALLOC_CTX *mem_ctx,
+                               struct wb_sid_object ***names)
 {
-       NTSTATUS status;
-
-       status = composite_wait(c);
-
+       NTSTATUS status = composite_wait(c);
        if (NT_STATUS_IS_OK(status)) {
-               struct finddcs_state *state = talloc_get_type(c->private_data,
-                                                             struct finddcs_state);
-               talloc_steal(mem_ctx, state->io->out.dcs);
+               struct lsa_lookupsids_state *state =
+                       talloc_get_type(c->private_data,
+                                       struct lsa_lookupsids_state);
+               *names = talloc_steal(mem_ctx, state->result);
        }
-
        talloc_free(c);
        return status;
 }
 
-NTSTATUS wb_finddcs(struct wb_finddcs *io, TALLOC_CTX *mem_ctx,
-                   struct event_context *ev)
-{
-       struct composite_context *c = wb_finddcs_send(io, ev);
-       return wb_finddcs_recv(c, mem_ctx);
-}
 
-struct get_schannel_creds_state {
+struct lsa_lookupnames_state {
        struct composite_context *ctx;
-       struct dcerpc_pipe *p;
-       struct wb_get_schannel_creds *io;
-       struct netr_ServerReqChallenge *r;
-
-       struct creds_CredentialState creds_state;
-       struct netr_Credential netr_cred;
-       uint32_t negotiate_flags;
-       struct netr_ServerAuthenticate2 *a;
+       uint32_t num_names;
+       struct lsa_LookupNames r;
+       struct lsa_TransSidArray sids;
+       struct lsa_RefDomainList *domains;
+       uint32_t count;
+       struct wb_sid_object **result;
 };
 
-static void get_schannel_creds_recv_auth(struct rpc_request *req);
-static void get_schannel_creds_recv_chal(struct rpc_request *req);
-static void get_schannel_creds_recv_pipe(struct composite_context *ctx);
+static void lsa_lookupnames_recv_sids(struct tevent_req *subreq);
 
-struct composite_context *wb_get_schannel_creds_send(struct wb_get_schannel_creds *io,
-                                                    struct event_context *ev)
+struct composite_context *wb_lsa_lookupnames_send(TALLOC_CTX *mem_ctx,
+                                                 struct dcerpc_pipe *lsa_pipe,
+                                                 struct policy_handle *handle,
+                                                 uint32_t num_names,
+                                                 const char **names)
 {
-       struct composite_context *result, *ctx;
-       struct get_schannel_creds_state *state;
+       struct composite_context *result;
+       struct lsa_lookupnames_state *state;
+       struct tevent_req *subreq;
+
+       struct lsa_String *lsa_names;
+       uint32_t i;
 
-       result = talloc_zero(NULL, struct composite_context);
+       result = composite_create(mem_ctx, lsa_pipe->conn->event_ctx);
        if (result == NULL) goto failed;
-       result->state = COMPOSITE_STATE_IN_PROGRESS;
-       result->event_ctx = ev;
 
-       state = talloc(result, struct get_schannel_creds_state);
+       state = talloc(result, struct lsa_lookupnames_state);
        if (state == NULL) goto failed;
        result->private_data = state;
+       state->ctx = result;
 
-       state->io = io;
+       state->sids.count = 0;
+       state->sids.sids = NULL;
+       state->num_names = num_names;
+       state->count = 0;
 
-       state->p = dcerpc_pipe_init(state, ev);
-       if (state->p == NULL) goto failed;
+       lsa_names = talloc_array(state, struct lsa_String, num_names);
+       if (lsa_names == NULL) goto failed;
 
-       ctx = dcerpc_pipe_open_smb_send(state->p->conn, state->io->in.tree,
-                                       "\\netlogon");
-       if (ctx == NULL) goto failed;
+       for (i=0; i<num_names; i++) {
+               lsa_names[i].string = names[i];
+       }
+
+       state->domains = talloc(state, struct lsa_RefDomainList);
+       if (state->domains == NULL) goto failed;
+
+       state->r.in.handle = handle;
+       state->r.in.num_names = num_names;
+       state->r.in.names = lsa_names;
+       state->r.in.sids = &state->sids;
+       state->r.in.level = 1;
+       state->r.in.count = &state->count;
+       state->r.out.count = &state->count;
+       state->r.out.sids = &state->sids;
+       state->r.out.domains = &state->domains;
+
+       subreq = dcerpc_lsa_LookupNames_r_send(state,
+                                              result->event_ctx,
+                                              lsa_pipe->binding_handle,
+                                              &state->r);
+       if (subreq == NULL) goto failed;
+       tevent_req_set_callback(subreq, lsa_lookupnames_recv_sids, state);
 
-       ctx->async.fn = get_schannel_creds_recv_pipe;
-       ctx->async.private_data = state;
-       state->ctx = result;
        return result;
 
  failed:
@@ -257,171 +255,215 @@ struct composite_context *wb_get_schannel_creds_send(struct wb_get_schannel_cred
        return NULL;
 }
 
-static void get_schannel_creds_recv_pipe(struct composite_context *ctx)
+static void lsa_lookupnames_recv_sids(struct tevent_req *subreq)
 {
-       struct get_schannel_creds_state *state =
-               talloc_get_type(ctx->async.private_data,
-                               struct get_schannel_creds_state);
-       struct rpc_request *req;
-
-       state->ctx->status = dcerpc_pipe_open_smb_recv(ctx);
-       if (!NT_STATUS_IS_OK(state->ctx->status)) goto done;
-
-       state->ctx->status = dcerpc_bind_auth_none(state->p,
-                                                  DCERPC_NETLOGON_UUID,
-                                                  DCERPC_NETLOGON_VERSION);
-       if (!NT_STATUS_IS_OK(state->ctx->status)) goto done;
-
-       state->r = talloc(state, struct netr_ServerReqChallenge);
-       if (state->r == NULL) {
-               state->ctx->status = NT_STATUS_NO_MEMORY;
-               goto done;
+       struct lsa_lookupnames_state *state =
+               tevent_req_callback_data(subreq,
+               struct lsa_lookupnames_state);
+       uint32_t i;
+
+       state->ctx->status = dcerpc_lsa_LookupNames_r_recv(subreq, state);
+       TALLOC_FREE(subreq);
+       if (!composite_is_ok(state->ctx)) return;
+       state->ctx->status = state->r.out.result;
+       if (!NT_STATUS_IS_OK(state->ctx->status) &&
+           !NT_STATUS_EQUAL(state->ctx->status, STATUS_SOME_UNMAPPED)) {
+               composite_error(state->ctx, state->ctx->status);
+               return;
        }
 
-       state->r->in.computer_name =
-               cli_credentials_get_workstation(state->io->in.creds);
-       state->r->in.server_name =
-               talloc_asprintf(state->r, "\\\\%s",
-                               dcerpc_server_name(state->p));
-       state->r->in.credentials = talloc(state->r, struct netr_Credential);
-       state->r->out.credentials = talloc(state->r, struct netr_Credential);
-
-       if ((state->r->in.server_name == NULL) ||
-           (state->r->in.credentials == NULL) ||
-           (state->r->out.credentials == NULL)) {
-               state->ctx->status = NT_STATUS_NO_MEMORY;
-               goto done;
-       }
-       generate_random_buffer(state->r->in.credentials->data,
-                              sizeof(state->r->in.credentials->data));
+       state->result = talloc_array(state, struct wb_sid_object *,
+                                    state->num_names);
+       if (composite_nomem(state->result, state->ctx)) return;
 
-       req = dcerpc_netr_ServerReqChallenge_send(state->p, state, state->r);
-       if (req == NULL) {
-               state->ctx->status = NT_STATUS_NO_MEMORY;
-               goto done;
-       }
+       for (i=0; i<state->num_names; i++) {
+               struct lsa_TranslatedSid *sid = &state->r.out.sids->sids[i];
+               struct lsa_RefDomainList *domains = state->domains;
+               struct lsa_DomainInfo *dom;
 
-       req->async.callback = get_schannel_creds_recv_chal;
-       req->async.private = state;
-       return;
+               state->result[i] = talloc_zero(state->result,
+                                              struct wb_sid_object);
+               if (composite_nomem(state->result[i], state->ctx)) return;
 
- done:
-       if (!NT_STATUS_IS_OK(state->ctx->status)) {
-               state->ctx->state = COMPOSITE_STATE_ERROR;
-       }
-       if ((state->ctx->state >= COMPOSITE_STATE_DONE) &&
-           (state->ctx->async.fn != NULL)) {
-               state->ctx->async.fn(state->ctx);
+               state->result[i]->type = sid->sid_type;
+               if (state->result[i]->type == SID_NAME_UNKNOWN) {
+                       continue;
+               }
+
+               if (sid->sid_index >= domains->count) {
+                       composite_error(state->ctx,
+                                       NT_STATUS_INVALID_PARAMETER);
+                       return;
+               }
+
+               dom = &domains->domains[sid->sid_index];
+
+               state->result[i]->sid = dom_sid_add_rid(state->result[i],
+                                                       dom->sid, sid->rid);
        }
+
+       composite_done(state->ctx);
 }
 
-static void get_schannel_creds_recv_chal(struct rpc_request *req)
+NTSTATUS wb_lsa_lookupnames_recv(struct composite_context *c,
+                                TALLOC_CTX *mem_ctx,
+                                struct wb_sid_object ***sids)
 {
-       struct get_schannel_creds_state *state =
-               talloc_get_type(req->async.private,
-                               struct get_schannel_creds_state);
-       const struct samr_Password *mach_pwd;
-
-       state->ctx->status = dcerpc_ndr_request_recv(req);
-       if (!NT_STATUS_IS_OK(state->ctx->status)) goto done;
-       state->ctx->status = state->r->out.result;
-       if (!NT_STATUS_IS_OK(state->ctx->status)) goto done;
-
-       mach_pwd = cli_credentials_get_nt_hash(state->io->in.creds, state);
-       if (mach_pwd == NULL) {
-               state->ctx->status = NT_STATUS_NO_MEMORY;
-               goto done;
+       NTSTATUS status = composite_wait(c);
+       if (NT_STATUS_IS_OK(status)) {
+               struct lsa_lookupnames_state *state =
+                       talloc_get_type(c->private_data,
+                                       struct lsa_lookupnames_state);
+               *sids = talloc_steal(mem_ctx, state->result);
        }
+       talloc_free(c);
+       return status;
+}
+struct samr_getuserdomgroups_state {
+       struct composite_context *ctx;
+       struct dcerpc_pipe *samr_pipe;
 
-       state->negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS;
+       uint32_t num_rids;
+       uint32_t *rids;
 
-       creds_client_init(&state->creds_state, state->r->in.credentials,
-                         state->r->out.credentials, mach_pwd,
-                         &state->netr_cred, state->negotiate_flags);
+       struct samr_RidWithAttributeArray *rid_array;
 
-       state->a = talloc(state, struct netr_ServerAuthenticate2);
-       if (state->a == NULL) {
-               state->ctx->status = NT_STATUS_NO_MEMORY;
-               goto done;
-       }
+       struct policy_handle *user_handle;
+       struct samr_OpenUser o;
+       struct samr_GetGroupsForUser g;
+       struct samr_Close c;
+};
 
-       state->a->in.server_name =
-               talloc_reference(state->a, state->r->in.server_name);
-       state->a->in.account_name =
-               cli_credentials_get_username(state->io->in.creds);
-       state->a->in.secure_channel_type =
-               cli_credentials_get_secure_channel_type(state->io->in.creds);
-       state->a->in.computer_name =
-               cli_credentials_get_workstation(state->io->in.creds);
-       state->a->in.negotiate_flags = &state->negotiate_flags;
-       state->a->out.negotiate_flags = &state->negotiate_flags;
-       state->a->in.credentials = &state->netr_cred;
-       state->a->out.credentials = &state->netr_cred;
-
-       req = dcerpc_netr_ServerAuthenticate2_send(state->p, state, state->a);
-       if (req == NULL) {
-               state->ctx->status = NT_STATUS_NO_MEMORY;
-               goto done;
-       }
+static void samr_usergroups_recv_open(struct tevent_req *subreq);
+static void samr_usergroups_recv_groups(struct tevent_req *subreq);
+static void samr_usergroups_recv_close(struct tevent_req *subreq);
 
-       req->async.callback = get_schannel_creds_recv_auth;
-       req->async.private = state;
-       return;
+struct composite_context *wb_samr_userdomgroups_send(TALLOC_CTX *mem_ctx,
+                                                    struct dcerpc_pipe *samr_pipe,
+                                                    struct policy_handle *domain_handle,
+                                                    uint32_t rid)
+{
+       struct composite_context *result;
+       struct samr_getuserdomgroups_state *state;
+       struct tevent_req *subreq;
 
-       state->io->out.netlogon = state->p;
-       state->ctx->state = COMPOSITE_STATE_DONE;
+       result = composite_create(mem_ctx, samr_pipe->conn->event_ctx);
+       if (result == NULL) goto failed;
 
- done:
-       if (!NT_STATUS_IS_OK(state->ctx->status)) {
-               state->ctx->state = COMPOSITE_STATE_ERROR;
-       }
-       if ((state->ctx->state >= COMPOSITE_STATE_DONE) &&
-           (state->ctx->async.fn != NULL)) {
-               state->ctx->async.fn(state->ctx);
-       }
-}
+       state = talloc(result, struct samr_getuserdomgroups_state);
+       if (state == NULL) goto failed;
+       result->private_data = state;
+       state->ctx = result;
 
-static void get_schannel_creds_recv_auth(struct rpc_request *req)
-{
-       struct get_schannel_creds_state *state =
-               talloc_get_type(req->async.private,
-                               struct get_schannel_creds_state);
+       state->samr_pipe = samr_pipe;
 
-       state->ctx->status = dcerpc_ndr_request_recv(req);
-       DEBUG(5, ("result: %s\n", nt_errstr(state->ctx->status)));
-       if (!NT_STATUS_IS_OK(state->ctx->status)) goto done;
-       state->ctx->status = state->a->out.result;
-       DEBUG(5, ("result: %s\n", nt_errstr(state->ctx->status)));
-       if (!NT_STATUS_IS_OK(state->ctx->status)) goto done;
+       state->user_handle = talloc(state, struct policy_handle);
+       if (state->user_handle == NULL) goto failed;
 
-       state->ctx->state = COMPOSITE_STATE_DONE;
+       state->o.in.domain_handle = domain_handle;
+       state->o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+       state->o.in.rid = rid;
+       state->o.out.user_handle = state->user_handle;
 
- done:
-       if (!NT_STATUS_IS_OK(state->ctx->status)) {
-               state->ctx->state = COMPOSITE_STATE_ERROR;
-       }
-       if ((state->ctx->state >= COMPOSITE_STATE_DONE) &&
-           (state->ctx->async.fn != NULL)) {
-               state->ctx->async.fn(state->ctx);
-       }
+       subreq = dcerpc_samr_OpenUser_r_send(state,
+                                            result->event_ctx,
+                                            state->samr_pipe->binding_handle,
+                                            &state->o);
+       if (subreq == NULL) goto failed;
+       tevent_req_set_callback(subreq, samr_usergroups_recv_open, state);
+
+       return result;
+
+ failed:
+       talloc_free(result);
+       return NULL;
+}
+                                             
+static void samr_usergroups_recv_open(struct tevent_req *subreq)
+{
+       struct samr_getuserdomgroups_state *state =
+               tevent_req_callback_data(subreq,
+               struct samr_getuserdomgroups_state);
+
+       state->ctx->status = dcerpc_samr_OpenUser_r_recv(subreq, state);
+       TALLOC_FREE(subreq);
+       if (!composite_is_ok(state->ctx)) return;
+       state->ctx->status = state->o.out.result;
+       if (!composite_is_ok(state->ctx)) return;
+
+       state->g.in.user_handle = state->user_handle;
+       state->g.out.rids = &state->rid_array;
+
+       subreq = dcerpc_samr_GetGroupsForUser_r_send(state,
+                                                    state->ctx->event_ctx,
+                                                    state->samr_pipe->binding_handle,
+                                                    &state->g);
+       if (composite_nomem(subreq, state->ctx)) return;
+       tevent_req_set_callback(subreq, samr_usergroups_recv_groups, state);
 }
 
-NTSTATUS wb_get_schannel_creds_recv(struct composite_context *c,
-                                   TALLOC_CTX *mem_ctx)
+static void samr_usergroups_recv_groups(struct tevent_req *subreq)
 {
-       NTSTATUS status = composite_wait(c);
-       struct get_schannel_creds_state *state =
-               talloc_get_type(c->private_data,
-                               struct get_schannel_creds_state);
-       state->io->out.netlogon = talloc_steal(mem_ctx, state->p);
-       talloc_free(c);
-       return status;
+       struct samr_getuserdomgroups_state *state =
+               tevent_req_callback_data(subreq,
+               struct samr_getuserdomgroups_state);
+
+       state->ctx->status = dcerpc_samr_GetGroupsForUser_r_recv(subreq, state);
+       TALLOC_FREE(subreq);
+       if (!composite_is_ok(state->ctx)) return;
+       state->ctx->status = state->g.out.result;
+       if (!composite_is_ok(state->ctx)) return;
+
+       state->c.in.handle = state->user_handle;
+       state->c.out.handle = state->user_handle;
+
+       subreq = dcerpc_samr_Close_r_send(state,
+                                         state->ctx->event_ctx,
+                                         state->samr_pipe->binding_handle,
+                                         &state->c);
+       if (composite_nomem(subreq, state->ctx)) return;
+       tevent_req_set_callback(subreq, samr_usergroups_recv_close, state);
+}
+
+static void samr_usergroups_recv_close(struct tevent_req *subreq)
+{
+        struct samr_getuserdomgroups_state *state =
+               tevent_req_callback_data(subreq,
+               struct samr_getuserdomgroups_state);
+
+       state->ctx->status = dcerpc_samr_Close_r_recv(subreq, state);
+       TALLOC_FREE(subreq);
+        if (!composite_is_ok(state->ctx)) return;
+        state->ctx->status = state->c.out.result;
+        if (!composite_is_ok(state->ctx)) return;
+
+       composite_done(state->ctx);
 }
 
-NTSTATUS wb_get_schannel_creds(struct wb_get_schannel_creds *io,
-                              TALLOC_CTX *mem_ctx,
-                              struct event_context *ev)
+NTSTATUS wb_samr_userdomgroups_recv(struct composite_context *ctx,
+                                   TALLOC_CTX *mem_ctx,
+                                   uint32_t *num_rids, uint32_t **rids)
 {
-       struct composite_context *c = wb_get_schannel_creds_send(io, ev);
-       return wb_get_schannel_creds_recv(c, mem_ctx);
+        struct samr_getuserdomgroups_state *state =
+                talloc_get_type(ctx->private_data,
+                                struct samr_getuserdomgroups_state);
+
+       uint32_t i;
+       NTSTATUS status = composite_wait(ctx);
+       if (!NT_STATUS_IS_OK(status)) goto done;
+
+       *num_rids = state->rid_array->count;
+       *rids = talloc_array(mem_ctx, uint32_t, *num_rids);
+       if (*rids == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       for (i=0; i<*num_rids; i++) {
+               (*rids)[i] = state->rid_array->rids[i].rid;
+       }
+
+ done:
+       talloc_free(ctx);
+       return status;
 }