r15625: Partial commit of my current work. It makes libnet api functions
authorRafal Szczesniak <mimir@samba.org>
Mon, 15 May 2006 21:49:27 +0000 (21:49 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 19:07:24 +0000 (14:07 -0500)
a bit more smart and more aware of what libnet_context can offer.
The context is a help when some of the arguments are not passed
(programmer counts on using sensible defaults) and stores some of
results so that similar subsequent calls don't need to reopen some
of policy handles, pipes, etc. again. It also helps to hide some
of details the library user don't really want to know much about.

Also, change domain open function to be part of public api, as
it is going to be used in ejsnet interface.

Note, this is work in progress. Comments are welcome.

rafal
(This used to be commit 1ed80c594c2f466e364a11194d6fdc30ac4a8f27)

source4/libnet/composite.h
source4/libnet/config.mk
source4/libnet/libnet.c
source4/libnet/libnet.h
source4/libnet/libnet_domain.c [moved from source4/libnet/domain.c with 50% similarity]
source4/libnet/libnet_domain.h [new file with mode: 0644]
source4/libnet/libnet_rpc.c
source4/libnet/libnet_user.c

index 916e3061249e8c15c8ac8a4519249513524341a8..516fdd4ef91f77f2a3c7fb02a30dca7c188bfcec 100644 (file)
@@ -111,14 +111,3 @@ struct libnet_rpc_usermod {
                } change;
        } in;
 };
-
-
-struct libnet_rpc_domain_open {
-       struct {
-               const char *domain_name;
-               uint32_t access_mask;
-       } in;
-       struct {
-               struct policy_handle domain_handle;
-       } out;
-};
index 05f170cbde08e3484005210c39499f37f7570452..4fa35110e62421befbc438a34c4c9eda75fb4288 100644 (file)
@@ -21,7 +21,7 @@ OBJ_FILES = \
                libnet_user.o \
                libnet_share.o \
                libnet_lookup.o \
+               libnet_domain.o \
                userinfo.o \
-               userman.o \
-               domain.o 
+               userman.o
 PUBLIC_DEPENDENCIES = dcerpc RPC_NDR_SAMR RPC_NDR_LSA RPC_NDR_SRVSVC RPC_NDR_DRSUAPI LIBCLI_COMPOSITE LIBCLI_RESOLVE LIBCLI_FINDDCS LIBSAMBA3 LIBCLI_CLDAP LIBCLI_FINDDCS gensec_schannel
index c3504168953134000cff140d18b0ec856614283b..8398e9ca17d63f51f9ac0900ff5075c787824c9e 100644 (file)
@@ -44,6 +44,9 @@ struct libnet_context *libnet_context_init(struct event_context *ev)
 
        /* name resolution methods */
        ctx->name_res_methods = str_list_copy(ctx, lp_name_resolve_order());
+
+       /* connected domain params */
+       ZERO_STRUCT(ctx->domain);
        
        return ctx;
 }
index 04428aa007162330f4d469c6a6d56865f80bf168..39dbb21f3286674decdc0de4e4791524068e86c1 100644 (file)
@@ -25,11 +25,18 @@ struct libnet_context {
         */
        struct cli_credentials *cred;
 
-       /* pipe */
+       /* pipes */
        struct dcerpc_pipe *pipe;
+       struct dcerpc_pipe *lsa_pipe;
+       struct dcerpc_pipe *samr_pipe;
+
+       /* opened handles and related properties */
+       struct {
+               const char *name;
+               uint32_t access_mask;
+               struct policy_handle handle;
+       } domain;
 
-       /* opened handles */
-       struct policy_handle domain_handle;
        struct policy_handle user_handle;
 
        /* name resolution methods */
@@ -49,5 +56,6 @@ struct libnet_context {
 #include "libnet/libnet_user.h"
 #include "libnet/libnet_share.h"
 #include "libnet/libnet_lookup.h"
+#include "libnet/libnet_domain.h"
 #include "libnet/composite.h"
 #include "libnet/libnet_proto.h"
similarity index 50%
rename from source4/libnet/domain.c
rename to source4/libnet/libnet_domain.c
index e48454f0e180d0ea285570cb233e78c827426b77..640cbade09861a7d2ea27afd9b40b5b97efcd46d 100644 (file)
 */
 
 /*
-  a composite function for domain handling
+  a composite function for domain handling on samr pipe
 */
 
 #include "includes.h"
 #include "libcli/composite/composite.h"
-#include "libnet/composite.h"
+#include "libnet/libnet.h"
 #include "librpc/gen_ndr/ndr_samr_c.h"
 
 static void domain_open_handler(struct rpc_request*);
 
-enum domain_open_stage { DOMOPEN_CONNECT, DOMOPEN_LOOKUP, DOMOPEN_OPEN };
+enum domain_open_stage { DOMOPEN_CONNECT, DOMOPEN_LOOKUP, DOMOPEN_OPEN,
+                        DOMOPEN_CLOSE_EXISTING, DOMOPEN_RPC_CONNECT };
 
 struct domain_open_state {
        enum domain_open_stage    stage;
+       struct libnet_context     *ctx;
        struct dcerpc_pipe        *pipe;
        struct rpc_request        *req;
+       struct composite_context  *rpcconn_req;
        struct samr_Connect       connect;
        struct samr_LookupDomain  lookup;
        struct samr_OpenDomain    open;
+       struct samr_Close         close;
+       struct libnet_RpcConnect  rpcconn;
        struct lsa_String         domain_name;
        uint32_t                  access_mask;
        struct policy_handle      connect_handle;
@@ -45,6 +50,72 @@ struct domain_open_state {
 };
 
 
+/**
+ * Stage 0.5 (optional): Connect to samr rpc pipe
+ */
+static void domain_open_rpc_connect(struct composite_context *ctx)
+{
+       struct composite_context *c;
+       struct domain_open_state *s;
+
+       c = talloc_get_type(ctx->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct domain_open_state);
+
+       c->status = libnet_RpcConnect_recv(ctx, s->ctx, c, &s->rpcconn);
+       if (!composite_is_ok(c)) return;
+
+       s->pipe = s->rpcconn.out.dcerpc_pipe;
+
+       /* preparing parameters for samr_Connect rpc call */
+       s->connect.in.system_name      = 0;
+       s->connect.in.access_mask      = s->access_mask;
+       s->connect.out.connect_handle  = &s->connect_handle;
+
+       /* send request */
+       s->req = dcerpc_samr_Connect_send(s->pipe, c, &s->connect);
+       if (composite_nomem(s->req, c)) return;
+
+       /* callback handler */
+       s->req->async.callback = domain_open_handler;
+       s->req->async.private  = c;
+       s->stage = DOMOPEN_CONNECT;
+}
+
+
+/**
+ * Stage 0.5 (optional): Close existing (in libnet context) domain
+ * handle
+ */
+static NTSTATUS domain_open_close(struct composite_context *c,
+                                 struct domain_open_state *s)
+{
+       /* receive samr_Close reply */
+       c->status = dcerpc_ndr_request_recv(s->req);
+       NT_STATUS_NOT_OK_RETURN(c->status);
+
+       /* reset domain handle and associated data in libnet_context */
+       s->ctx->domain.name        = NULL;
+       s->ctx->domain.access_mask = 0;
+       ZERO_STRUCT(s->ctx->domain.handle);
+
+       /* preparing parameters for samr_Connect rpc call */
+       s->connect.in.system_name      = 0;
+       s->connect.in.access_mask      = s->access_mask;
+       s->connect.out.connect_handle  = &s->connect_handle;
+       
+       /* send request */
+       s->req = dcerpc_samr_Connect_send(s->pipe, c, &s->connect);
+       if (s->req == NULL) return NT_STATUS_NO_MEMORY;
+
+       /* callback handler */
+       s->req->async.callback = domain_open_handler;
+       s->req->async.private  = c;
+       s->stage = DOMOPEN_CONNECT;
+       
+       return NT_STATUS_OK;
+}
+
+
 /**
  * Stage 1: Connect to SAM server.
  */
@@ -145,48 +216,106 @@ static void domain_open_handler(struct rpc_request *req)
        case DOMOPEN_OPEN:
                c->status = domain_open_open(c, s);
                break;
+       case DOMOPEN_CLOSE_EXISTING:
+               c->status = domain_open_close(c, s);
+               break;
+       case DOMOPEN_RPC_CONNECT:
+               /* this state shouldn't be handled here */
+               c->status = NT_STATUS_UNSUCCESSFUL;
+               break;
        }
 
        if (!NT_STATUS_IS_OK(c->status)) {
                c->state = COMPOSITE_STATE_ERROR;
        }
+
+       if (c->state == COMPOSITE_STATE_DONE) {
+               composite_done(c);
+       }
 }
 
 
 /**
  * Sends asynchronous domain_open request
  *
- * @param p dce/rpc call pipe 
+ * @param ctx initialised libnet context
  * @param io arguments and results of the call
+ * @param monitor pointer to monitor function that is passed monitor message
  */
-struct composite_context *libnet_rpc_domain_open_send(struct dcerpc_pipe *p,
-                                                     struct libnet_rpc_domain_open *io,
-                                                     void (*monitor)(struct monitor_msg*))
+
+struct composite_context *libnet_DomainOpen_send(struct libnet_context *ctx,
+                                                struct libnet_DomainOpen *io,
+                                                void (*monitor)(struct monitor_msg*))
 {
        struct composite_context *c;
        struct domain_open_state *s;
 
-       c = talloc_zero(p, struct composite_context);
-       if (c == NULL) goto failure;
+       c = talloc_zero(ctx, struct composite_context);
+       if (c == NULL) return NULL;
 
        s = talloc_zero(c, struct domain_open_state);
-       if (s == NULL) goto failure;
+       if (composite_nomem(s, c)) return c;
 
-       c->state       = COMPOSITE_STATE_IN_PROGRESS;
-       c->private_data= s;
-       c->event_ctx   = dcerpc_event_context(p);
+       c->state        = COMPOSITE_STATE_IN_PROGRESS;
+       c->private_data = s;
+       c->event_ctx    = ctx->event_ctx;
 
-       s->pipe                = p;
+       s->ctx                 = ctx;
+       s->pipe                = ctx->samr_pipe;
        s->access_mask         = io->in.access_mask;
        s->domain_name.string  = io->in.domain_name;
 
-       /* preparing parameters to send rpc request */
+       if (ctx->samr_pipe == NULL) {
+               s->rpcconn.level           = LIBNET_RPC_CONNECT_DC;
+               s->rpcconn.in.name         = io->in.domain_name;
+               s->rpcconn.in.dcerpc_iface = &dcerpc_table_samr;
+
+               s->rpcconn_req = libnet_RpcConnect_send(ctx, c, &s->rpcconn);
+               if (composite_nomem(s->rpcconn_req, c)) return c;
+
+               s->rpcconn_req->async.fn = domain_open_rpc_connect;
+               s->rpcconn_req->async.private_data  = c;
+               s->stage = DOMOPEN_RPC_CONNECT;
+
+               return c;
+       }
+
+       /* libnet context's domain handle is not empty, so check out what
+          was opened first, before doing anything */
+       if (!policy_handle_empty(&ctx->domain.handle)) {
+               if (strequal(ctx->domain.name, io->in.domain_name) &&
+                   ctx->domain.access_mask == io->in.access_mask) {
+
+                       /* this domain is already opened */
+                       composite_done(c);
+                       return c;
+
+               } else {
+                       /* another domain or access rights have been
+                          requested - close the existing handle first */
+                       s->close.in.handle = &ctx->domain.handle;
+
+                       /* send request to close domain handle */
+                       s->req = dcerpc_samr_Close_send(s->pipe, c, &s->close);
+                       if (composite_nomem(s->req, c)) return c;
+
+                       /* callback handler */
+                       s->req->async.callback = domain_open_handler;
+                       s->req->async.private  = c;
+                       s->stage = DOMOPEN_CLOSE_EXISTING;
+
+                       return c;
+               }
+       }
+
+       /* preparing parameters for samr_Connect rpc call */
        s->connect.in.system_name      = 0;
        s->connect.in.access_mask      = s->access_mask;
        s->connect.out.connect_handle  = &s->connect_handle;
        
        /* send request */
-       s->req = dcerpc_samr_Connect_send(p, c, &s->connect);
+       s->req = dcerpc_samr_Connect_send(s->pipe, c, &s->connect);
+       if (composite_nomem(s->req, c)) return c;
 
        /* callback handler */
        s->req->async.callback = domain_open_handler;
@@ -194,10 +323,6 @@ struct composite_context *libnet_rpc_domain_open_send(struct dcerpc_pipe *p,
        s->stage = DOMOPEN_CONNECT;
 
        return c;
-
-failure:
-       talloc_free(c);
-       return NULL;
 }
 
 
@@ -205,12 +330,14 @@ failure:
  * Waits for and receives result of asynchronous domain_open call
  * 
  * @param c composite context returned by asynchronous domain_open call
+ * @param ctx initialised libnet context
  * @param mem_ctx memory context of the call
  * @param io pointer to results (and arguments) of the call
  * @return nt status code of execution
  */
-NTSTATUS libnet_rpc_domain_open_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
-                                    struct libnet_rpc_domain_open *io)
+
+NTSTATUS libnet_DomainOpen_recv(struct composite_context *c, struct libnet_context *ctx,
+                               TALLOC_CTX *mem_ctx, struct libnet_DomainOpen *io)
 {
        NTSTATUS status;
        struct domain_open_state *s;
@@ -221,6 +348,12 @@ NTSTATUS libnet_rpc_domain_open_recv(struct composite_context *c, TALLOC_CTX *me
        if (NT_STATUS_IS_OK(status) && io) {
                s = talloc_get_type(c->private_data, struct domain_open_state);
                io->out.domain_handle = s->domain_handle;
+
+               /* store the resulting handle and related data for use by other
+                  libnet functions */
+               ctx->domain.handle      = s->domain_handle;
+               ctx->domain.name        = talloc_strdup(ctx, s->domain_name.string);
+               ctx->domain.access_mask = s->access_mask;
        }
 
        talloc_free(c);
@@ -231,15 +364,16 @@ NTSTATUS libnet_rpc_domain_open_recv(struct composite_context *c, TALLOC_CTX *me
 /**
  * Synchronous version of domain_open call
  *
- * @param pipe dce/rpc call pipe
+ * @param ctx initialised libnet context
  * @param mem_ctx memory context for the call
  * @param io arguments and results of the call
  * @return nt status code of execution
  */
-NTSTATUS libnet_rpc_domain_open(struct dcerpc_pipe *p,
-                               TALLOC_CTX *mem_ctx,
-                               struct libnet_rpc_domain_open *io)
+
+NTSTATUS libnet_DomainOpen(struct libnet_context *ctx,
+                          TALLOC_CTX *mem_ctx,
+                          struct libnet_DomainOpen *io)
 {
-       struct composite_context *c = libnet_rpc_domain_open_send(p, io, NULL);
-       return libnet_rpc_domain_open_recv(c, mem_ctx, io);
+       struct composite_context *c = libnet_DomainOpen_send(ctx, io, NULL);
+       return libnet_DomainOpen_recv(c, ctx, mem_ctx, io);
 }
diff --git a/source4/libnet/libnet_domain.h b/source4/libnet/libnet_domain.h
new file mode 100644 (file)
index 0000000..6dc4ed3
--- /dev/null
@@ -0,0 +1,34 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Copyright (C) Rafal Szczesniak 2005
+   
+   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
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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.
+*/
+
+
+/*
+ * struct definition for opening a domain
+ */
+
+struct libnet_DomainOpen {
+       struct {
+               const char *domain_name;
+               uint32_t access_mask;
+       } in;
+       struct {
+               struct policy_handle domain_handle;
+       } out;
+};
index 3e66a5c27c795729c8737b3eca030592250a8cc1..b1853802950fa4ad7112de7e3ae9bab10974cfb4 100644 (file)
@@ -24,6 +24,7 @@
 #include "libcli/libcli.h"
 #include "libcli/composite/composite.h"
 #include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_samr.h"
 
 
 struct rpc_connect_srv_state {
@@ -137,7 +138,15 @@ static NTSTATUS libnet_RpcConnectSrv_recv(struct composite_context *c,
                /* move the returned rpc pipe between memory contexts */
                s = talloc_get_type(c->private_data, struct rpc_connect_srv_state);
                r->out.dcerpc_pipe = talloc_steal(mem_ctx, s->r.out.dcerpc_pipe);
-               ctx->pipe = r->out.dcerpc_pipe;
+
+               /* reference created pipe structure to long-term libnet_context
+                  so that it can be used by other api functions even after short-term
+                  mem_ctx is freed */
+               if (r->in.dcerpc_iface == &dcerpc_table_samr) {
+                       ctx->samr_pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+               } else {
+                       ctx->pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+               }
        }
 
        talloc_free(c);
@@ -301,7 +310,15 @@ static NTSTATUS libnet_RpcConnectDC_recv(struct composite_context *c,
                /* move connected rpc pipe between memory contexts */
                s = talloc_get_type(c->private_data, struct rpc_connect_dc_state);
                r->out.dcerpc_pipe = talloc_steal(mem_ctx, s->r.out.dcerpc_pipe);
-               ctx->pipe = r->out.dcerpc_pipe;
+
+               /* reference created pipe structure to long-term libnet_context
+                  so that it can be used by other api functions even after short-term
+                  mem_ctx is freed */
+               if (r->in.dcerpc_iface == &dcerpc_table_samr) {
+                       ctx->samr_pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+               } else {
+                       ctx->pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+               }
        }
 
        talloc_free(c);
@@ -639,9 +656,17 @@ static NTSTATUS libnet_RpcConnectDCInfo_recv(struct composite_context *c, struct
                r->out.guid         = talloc_steal(mem_ctx, s->r.out.guid);
                r->out.domain_name  = talloc_steal(mem_ctx, s->r.out.domain_name);
                r->out.domain_sid   = talloc_steal(mem_ctx, s->r.out.domain_sid);
+
                r->out.dcerpc_pipe  = talloc_steal(mem_ctx, s->r.out.dcerpc_pipe);
 
-               r->out.error_string = NULL;
+               /* reference created pipe structure to long-term libnet_context
+                  so that it can be used by other api functions even after short-term
+                  mem_ctx is freed */
+               if (r->in.dcerpc_iface == &dcerpc_table_samr) {
+                       ctx->samr_pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+               } else {
+                       ctx->pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+               }
        }
 
        talloc_free(c);
index 1fca102d177acc9648b1a02f7a32ce192dccb1c7..53364f7f326b6eaf48e44f0ae20e0fe10d8ae068 100644 (file)
 
 #include "includes.h"
 #include "libnet/libnet.h"
+#include "libcli/composite/composite.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/ndr/libndr.h"
 #include "librpc/gen_ndr/ndr_samr.h"
 
 
+struct create_user_state {
+       struct libnet_CreateUser r;
+       struct libnet_DomainOpen domain_open;
+       struct libnet_rpc_useradd user_add;
+       struct libnet_context *ctx;
+
+       /* information about the progress */
+       void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_rpc_useradd(struct composite_context *ctx);
+static void continue_domain_open(struct composite_context *ctx);
+
+
+struct composite_context* libnet_CreateUser_send(struct libnet_context *ctx,
+                                                TALLOC_CTX *mem_ctx,
+                                                struct libnet_CreateUser *r,
+                                                void (*monitor)(struct monitor_msg*))
+{
+       struct composite_context *c;
+       struct create_user_state *s;
+       struct composite_context *create_req;
+       struct composite_context *domopen_req;
+
+       c = talloc_zero(mem_ctx, struct composite_context);
+       if (c == NULL) return NULL;
+
+       s = talloc_zero(c, struct create_user_state);
+       if (composite_nomem(s, c)) return c;
+
+       c->state = COMPOSITE_STATE_IN_PROGRESS;
+       c->private_data = s;
+       c->event_ctx = ctx->event_ctx;
+
+       s->ctx = ctx;
+       s->r   = *r;
+
+       if (s->r.in.domain_name == NULL) {
+               
+               if (policy_handle_empty(&ctx->domain.handle)) {
+                       s->domain_open.in.domain_name = cli_credentials_get_domain(ctx->cred);
+                       s->domain_open.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+                       
+                       domopen_req = libnet_DomainOpen_send(ctx, &s->domain_open, monitor);
+                       if (composite_nomem(domopen_req, c)) return c;
+                       
+                       composite_continue(c, domopen_req, continue_domain_open, c);
+                       return c;
+               } else {
+                       /* no domain name provided - neither in io structure nor default
+                          stored in libnet context - report an error */
+                       composite_error(c, NT_STATUS_INVALID_PARAMETER);
+                       return c;
+               }
+
+       } else {
+               
+               if (policy_handle_empty(&ctx->domain.handle) ||
+                   !strequal(s->r.in.domain_name, ctx->domain.name)) {
+                       s->domain_open.in.domain_name = s->r.in.domain_name;
+                       s->domain_open.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+
+                       domopen_req = libnet_DomainOpen_send(ctx, &s->domain_open, monitor);
+                       if (composite_nomem(domopen_req, c)) return c;
+                       
+                       composite_continue(c, domopen_req, continue_domain_open, c);
+                       return c;
+               }
+       }
+       
+       s->user_add.in.username       = r->in.user_name;
+       s->user_add.in.domain_handle  = ctx->domain.handle;
+
+       create_req = libnet_rpc_useradd_send(ctx->samr_pipe, &s->user_add, monitor);
+       if (composite_nomem(create_req, c)) return c;
+
+       composite_continue(c, create_req, continue_rpc_useradd, c);
+       return c;
+}
+
+
+static void continue_domain_open(struct composite_context *ctx)
+{
+       struct composite_context *c;
+       struct create_user_state *s;
+       struct composite_context *create_req;
+       struct monitor_msg msg;
+
+       c = talloc_get_type(ctx->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct create_user_state);
+
+       c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+       if (!composite_is_ok(c)) return;
+
+       if (s->monitor_fn) s->monitor_fn(&msg);
+       
+       s->user_add.in.username       = s->r.in.user_name;
+       s->user_add.in.domain_handle  = s->ctx->domain.handle;
+
+       create_req = libnet_rpc_useradd_send(s->ctx->samr_pipe, &s->user_add, s->monitor_fn);
+       if (composite_nomem(create_req, c)) return;
+       
+       composite_continue(c, create_req, continue_rpc_useradd, c);
+}
+
+
+static void continue_rpc_useradd(struct composite_context *ctx)
+{
+       struct composite_context *c;
+       struct create_user_state *s;
+       struct monitor_msg msg;
+
+       c = talloc_get_type(ctx->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct create_user_state);
+
+       c->status = libnet_rpc_useradd_recv(ctx, c, &s->user_add);
+       if (!composite_is_ok(c)) return;
+
+       if (s->monitor_fn) s->monitor_fn(&msg);
+       composite_done(c);
+}
+
+
+NTSTATUS libnet_CreateUser_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+                               struct libnet_CreateUser *r)
+{
+       NTSTATUS status;
+       struct create_user_state *s;
+
+       status = composite_wait(c);
+       if (!NT_STATUS_IS_OK(status)) {
+               s = talloc_get_type(c->private_data, struct create_user_state);
+               r->out.error_string = talloc_steal(mem_ctx, s->r.out.error_string);
+       }
+
+       r->out.error_string = NULL;
+       return status;
+}
+
+
+NTSTATUS libnet_CreateUser(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+                          struct libnet_CreateUser *r)
+{
+       struct composite_context *c;
+
+       c = libnet_CreateUser_send(ctx, mem_ctx, r, NULL);
+       return libnet_CreateUser_recv(c, mem_ctx, r);
+}
+
+
+#ifdef OBSOLETE
 NTSTATUS libnet_CreateUser(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_CreateUser *r)
 {
        NTSTATUS status;
        struct libnet_RpcConnect cn;
-       struct libnet_rpc_domain_open dom_io;
+       struct libnet_DomainOpen dom_io;
        struct libnet_rpc_useradd user_io;
        
        /* connect rpc service of remote DC */
@@ -44,13 +199,11 @@ NTSTATUS libnet_CreateUser(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
                return status;
        }
 
-       ctx->pipe = cn.out.dcerpc_pipe;
-
        /* open connected domain */
        dom_io.in.domain_name   = r->in.domain_name;
        dom_io.in.access_mask   = SEC_FLAG_MAXIMUM_ALLOWED;
        
-       status = libnet_rpc_domain_open(ctx->pipe, mem_ctx, &dom_io);
+       status = libnet_DomainOpen(ctx, mem_ctx, &dom_io);
        if (!NT_STATUS_IS_OK(status)) {
                r->out.error_string = talloc_asprintf(mem_ctx,
                                                      "Creating user account failed: %s\n",
@@ -58,13 +211,11 @@ NTSTATUS libnet_CreateUser(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
                return status;
        }
 
-       ctx->domain_handle = dom_io.out.domain_handle;
-
        /* create user */
        user_io.in.username       = r->in.user_name;
        user_io.in.domain_handle  = dom_io.out.domain_handle;
 
-       status = libnet_rpc_useradd(ctx->pipe, mem_ctx, &user_io);
+       status = libnet_rpc_useradd(ctx, mem_ctx, &user_io);
        if (!NT_STATUS_IS_OK(status)) {
                r->out.error_string = talloc_asprintf(mem_ctx,
                                                      "Creating user account failed: %s\n",
@@ -76,12 +227,14 @@ NTSTATUS libnet_CreateUser(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
 
        return status;
 }
+#endif
+
 
 NTSTATUS libnet_DeleteUser(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_DeleteUser *r)
 {
        NTSTATUS status;
        struct libnet_RpcConnect cn;
-       struct libnet_rpc_domain_open dom_io;
+       struct libnet_DomainOpen dom_io;
        struct libnet_rpc_userdel user_io;
        
        /* connect rpc service of remote DC */
@@ -97,13 +250,11 @@ NTSTATUS libnet_DeleteUser(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
                return status;
        }
 
-       ctx->pipe = cn.out.dcerpc_pipe;
-
        /* open connected domain */
        dom_io.in.domain_name   = r->in.domain_name;
        dom_io.in.access_mask   = SEC_FLAG_MAXIMUM_ALLOWED;
        
-       status = libnet_rpc_domain_open(ctx->pipe, mem_ctx, &dom_io);
+       status = libnet_DomainOpen(ctx, mem_ctx, &dom_io);
        if (!NT_STATUS_IS_OK(status)) {
                r->out.error_string = talloc_asprintf(mem_ctx,
                                                      "Opening domain to delete user account failed: %s\n",
@@ -111,13 +262,11 @@ NTSTATUS libnet_DeleteUser(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru
                return status;
        }
 
-       ctx->domain_handle = dom_io.out.domain_handle;
-
        /* create user */
        user_io.in.username       = r->in.user_name;
        user_io.in.domain_handle  = dom_io.out.domain_handle;
 
-       status = libnet_rpc_userdel(ctx->pipe, mem_ctx, &user_io);
+       status = libnet_rpc_userdel(ctx, mem_ctx, &user_io);
        if (!NT_STATUS_IS_OK(status)) {
                r->out.error_string = talloc_asprintf(mem_ctx,
                                                      "Deleting user account failed: %s\n",