Merge branch 'master' of ssh://git.samba.org/data/git/samba
[ira/wip.git] / source4 / libnet / userman.c
index 13d9b1330b635b933432d79d1ac39f31d6bfccbd..c638d8af32115aaf2c7baeb6e07b409e4c88c03e 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,
@@ -14,8 +14,7 @@
    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/>.
 */
 
 /*
 */
 
 #include "includes.h"
-#include "libcli/raw/libcliraw.h"
 #include "libcli/composite/composite.h"
-#include "libcli/composite/monitor.h"
-#include "librpc/gen_ndr/ndr_samr.h"
 #include "libnet/composite.h"
 #include "libnet/userman.h"
 #include "libnet/userinfo.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "libnet/libnet_proto.h"
 
 /*
- * Composite user add function
+ * Composite USER ADD functionality
  */
 
-static void useradd_handler(struct rpc_request*);
-
-enum useradd_stage { USERADD_CREATE };
-
 struct useradd_state {
-       enum useradd_stage       stage;
        struct dcerpc_pipe       *pipe;
        struct rpc_request       *req;
        struct policy_handle     domain_handle;
        struct samr_CreateUser   createuser;
        struct policy_handle     user_handle;
        uint32_t                 user_rid;
+
+       /* information about the progress */
+       void (*monitor_fn)(struct monitor_msg *);
 };
 
 
+static void continue_useradd_create(struct rpc_request *req);
+
+
 /**
  * Stage 1 (and the only one for now): Create user account.
  */
-static NTSTATUS useradd_create(struct composite_context *c,
-                              struct useradd_state *s)
+static void continue_useradd_create(struct rpc_request *req)
 {
+       struct composite_context *c;
+       struct useradd_state *s;
+
+       c = talloc_get_type(req->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct useradd_state);
+
+       /* check rpc layer status code */
        c->status = dcerpc_ndr_request_recv(s->req);
-       NT_STATUS_NOT_OK_RETURN(c->status);
-       
-       c->state = SMBCLI_REQUEST_DONE;
-       return NT_STATUS_OK;
-}
+       if (!composite_is_ok(c)) return;
 
+       /* check create user call status code */
+       c->status = s->createuser.out.result;
 
-/**
- * Event handler for asynchronous request. Handles transition through
- * intermediate stages of the call.
- *
- * @param req rpc call context
- */
-static void useradd_handler(struct rpc_request *req)
-{
-       struct composite_context *c = req->async.private;
-       struct useradd_state *s = talloc_get_type(c->private, struct useradd_state);
-       struct monitor_msg msg;
-       struct msg_rpc_create_user *rpc_create;
-       
-       switch (s->stage) {
-       case USERADD_CREATE:
-               c->status = useradd_create(c, s);
-
-               msg.type = rpc_create_user;
-               rpc_create = talloc(s, struct msg_rpc_create_user);
-               rpc_create->rid = *s->createuser.out.rid;
-               msg.data = (void*)rpc_create;
-               msg.data_size = sizeof(*rpc_create);
-               break;
-       }
+       /* get created user account data */
+       s->user_handle = *s->createuser.out.user_handle;
+       s->user_rid    = *s->createuser.out.rid;
 
-       if (!NT_STATUS_IS_OK(c->status)) {
-               c->state = SMBCLI_REQUEST_ERROR;
-       }
+       /* issue a monitor message */
+       if (s->monitor_fn) {
+               struct monitor_msg msg;
+               struct msg_rpc_create_user rpc_create;
 
-       if (c->monitor_fn) {
-               c->monitor_fn(&msg);
-       }
+               rpc_create.rid = *s->createuser.out.rid;
 
-       if (c->state >= SMBCLI_REQUEST_DONE &&
-           c->async.fn) {
-               c->async.fn(c);
+               msg.type      = mon_SamrCreateUser;
+               msg.data      = (void*)&rpc_create;
+               msg.data_size = sizeof(rpc_create);
+               
+               s->monitor_fn(&msg);
        }
+       
+       composite_done(c);
 }
 
 
@@ -109,6 +94,7 @@ static void useradd_handler(struct rpc_request *req)
  *
  * @param p dce/rpc call pipe 
  * @param io arguments and results of the call
+ * @param monitor monitor function for providing information about the progress
  */
 
 struct composite_context *libnet_rpc_useradd_send(struct dcerpc_pipe *p,
@@ -117,41 +103,41 @@ struct composite_context *libnet_rpc_useradd_send(struct dcerpc_pipe *p,
 {
        struct composite_context *c;
        struct useradd_state *s;
-       
-       c = talloc_zero(p, struct composite_context);
-       if (c == NULL) goto failure;
+
+       if (!p || !io) return NULL;
+
+       /* composite allocation and setup */
+       c = composite_create(p, dcerpc_event_context(p));
+       if (c == NULL) return NULL;
        
        s = talloc_zero(c, struct useradd_state);
-       if (s == NULL) goto failure;
+       if (composite_nomem(s, c)) return c;
        
+       c->private_data = s;
+
+       /* put passed arguments to the state structure */
        s->domain_handle = io->in.domain_handle;
        s->pipe          = p;
+       s->monitor_fn    = monitor;
        
-       c->state       = SMBCLI_REQUEST_SEND;
-       c->private     = s;
-       c->event_ctx   = dcerpc_event_context(p);
-       c->monitor_fn  = monitor;
-
        /* preparing parameters to send rpc request */
        s->createuser.in.domain_handle         = &io->in.domain_handle;
+
        s->createuser.in.account_name          = talloc_zero(c, struct lsa_String);
+       if (composite_nomem(s->createuser.in.account_name, c)) return c;
+
        s->createuser.in.account_name->string  = talloc_strdup(c, io->in.username);
+       if (composite_nomem(s->createuser.in.account_name->string, c)) return c;
+
        s->createuser.out.user_handle          = &s->user_handle;
        s->createuser.out.rid                  = &s->user_rid;
 
-       /* send request */
+       /* send the request */
        s->req = dcerpc_samr_CreateUser_send(p, c, &s->createuser);
+       if (composite_nomem(s->req, c)) return c;
 
-       /* callback handler */
-       s->req->async.callback = useradd_handler;
-       s->req->async.private  = c;
-       s->stage = USERADD_CREATE;
-
+       composite_continue_rpc(c, s->req, continue_useradd_create, c);
        return c;
-       
-failure:
-       talloc_free(c);
-       return NULL;
 }
 
 
@@ -165,7 +151,7 @@ failure:
  */
 
 NTSTATUS libnet_rpc_useradd_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
-                                   struct libnet_rpc_useradd *io)
+                                struct libnet_rpc_useradd *io)
 {
        NTSTATUS status;
        struct useradd_state *s;
@@ -174,7 +160,7 @@ NTSTATUS libnet_rpc_useradd_recv(struct composite_context *c, TALLOC_CTX *mem_ct
        
        if (NT_STATUS_IS_OK(status) && io) {
                /* get and return result of the call */
-               s = talloc_get_type(c->private, struct useradd_state);
+               s = talloc_get_type(c->private_data, struct useradd_state);
                io->out.user_handle = s->user_handle;
        }
 
@@ -192,165 +178,183 @@ NTSTATUS libnet_rpc_useradd_recv(struct composite_context *c, TALLOC_CTX *mem_ct
  * @return nt status code of execution
  */
 
-NTSTATUS libnet_rpc_useradd(struct dcerpc_pipe *pipe,
-                              TALLOC_CTX *mem_ctx,
-                              struct libnet_rpc_useradd *io)
+NTSTATUS libnet_rpc_useradd(struct dcerpc_pipe *p,
+                           TALLOC_CTX *mem_ctx,
+                           struct libnet_rpc_useradd *io)
 {
-       struct composite_context *c = libnet_rpc_useradd_send(pipe, io, NULL);
+       struct composite_context *c = libnet_rpc_useradd_send(p, io, NULL);
        return libnet_rpc_useradd_recv(c, mem_ctx, io);
 }
 
 
+
 /*
- * Composite user delete function
+ * Composite USER DELETE functionality
  */
 
-static void userdel_handler(struct rpc_request*);
-
-enum userdel_stage { USERDEL_LOOKUP, USERDEL_OPEN, USERDEL_DELETE };
 
 struct userdel_state {
-       enum userdel_stage        stage;
        struct dcerpc_pipe        *pipe;
-       struct rpc_request        *req;
        struct policy_handle      domain_handle;
        struct policy_handle      user_handle;
        struct samr_LookupNames   lookupname;
        struct samr_OpenUser      openuser;
        struct samr_DeleteUser    deleteuser;
+
+       /* information about the progress */
+       void (*monitor_fn)(struct monitor_msg *);
 };
 
 
+static void continue_userdel_name_found(struct rpc_request *req);
+static void continue_userdel_user_opened(struct rpc_request* req);
+static void continue_userdel_deleted(struct rpc_request *req);
+
+
 /**
  * Stage 1: Lookup the user name and resolve it to rid
  */
-static NTSTATUS userdel_lookup(struct composite_context *c,
-                              struct userdel_state *s)
+static void continue_userdel_name_found(struct rpc_request *req)
 {
-       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       struct composite_context *c;
+       struct userdel_state *s;
+       struct rpc_request *openuser_req;
+       struct monitor_msg msg;
 
-       c->status = dcerpc_ndr_request_recv(s->req);
-       NT_STATUS_NOT_OK_RETURN(c->status);
-       
-       if (!s->lookupname.out.rids.count) {
-               /* TODO: no such user */
-               status = NT_STATUS_NO_SUCH_USER;
+       c = talloc_get_type(req->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct userdel_state);
+
+       /* receive samr_LookupNames result */
+       c->status = dcerpc_ndr_request_recv(req);
+       if (!composite_is_ok(c)) return;
+
+       c->status = s->lookupname.out.result;
+       if (!NT_STATUS_IS_OK(c->status)) {
+               composite_error(c, c->status);
+               return;
+       }
 
-       } else if (!s->lookupname.out.rids.count > 1) {
-               /* TODO: ambiguous username */
-               status = NT_STATUS_INVALID_ACCOUNT_NAME;
+       /* what to do when there's no user account to delete
+          and what if there's more than one rid resolved */
+       if (!s->lookupname.out.rids->count) {
+               c->status = NT_STATUS_NO_SUCH_USER;
+               composite_error(c, c->status);
+               return;
+
+       } else if (!s->lookupname.out.rids->count > 1) {
+               c->status = NT_STATUS_INVALID_ACCOUNT_NAME;
+               composite_error(c, c->status);
+               return;
        }
-       
+
+       /* issue a monitor message */
+       if (s->monitor_fn) {
+               struct msg_rpc_lookup_name msg_lookup;
+
+               msg_lookup.rid   = s->lookupname.out.rids->ids;
+               msg_lookup.count = s->lookupname.out.rids->count;
+
+               msg.type      = mon_SamrLookupName;
+               msg.data      = (void*)&msg_lookup;
+               msg.data_size = sizeof(msg_lookup);
+               s->monitor_fn(&msg);
+       }
+
+       /* prepare the arguments for rpc call */
        s->openuser.in.domain_handle = &s->domain_handle;
-       s->openuser.in.rid           = s->lookupname.out.rids.ids[0];
+       s->openuser.in.rid           = s->lookupname.out.rids->ids[0];
        s->openuser.in.access_mask   = SEC_FLAG_MAXIMUM_ALLOWED;
        s->openuser.out.user_handle  = &s->user_handle;
 
-       s->req = dcerpc_samr_OpenUser_send(s->pipe, c, &s->openuser);
-       
-       s->req->async.callback = userdel_handler;
-       s->req->async.private  = c;
-       s->stage = USERDEL_OPEN;
-       
-       return NT_STATUS_OK;
+       /* send rpc request */
+       openuser_req = dcerpc_samr_OpenUser_send(s->pipe, c, &s->openuser);
+       if (composite_nomem(openuser_req, c)) return;
+
+       composite_continue_rpc(c, openuser_req, continue_userdel_user_opened, c);
 }
 
 
 /**
  * Stage 2: Open user account.
  */
-static NTSTATUS userdel_open(struct composite_context *c,
-                            struct userdel_state *s)
+static void continue_userdel_user_opened(struct rpc_request* req)
 {
-       c->status = dcerpc_ndr_request_recv(s->req);
-       NT_STATUS_NOT_OK_RETURN(c->status);
+       struct composite_context *c;
+       struct userdel_state *s;
+       struct rpc_request *deluser_req;
+       struct monitor_msg msg;
+
+       c = talloc_get_type(req->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct userdel_state);
+
+       /* receive samr_OpenUser result */
+       c->status = dcerpc_ndr_request_recv(req);
+       if (!composite_is_ok(c)) return;
+
+       c->status = s->openuser.out.result;
+       if (!NT_STATUS_IS_OK(c->status)) {
+               composite_error(c, c->status);
+               return;
+       }
        
+       /* issue a monitor message */
+       if (s->monitor_fn) {
+               struct msg_rpc_open_user msg_open;
+
+               msg_open.rid         = s->openuser.in.rid;
+               msg_open.access_mask = s->openuser.in.access_mask;
+
+               msg.type      = mon_SamrOpenUser;
+               msg.data      = (void*)&msg_open;
+               msg.data_size = sizeof(msg_open);
+               s->monitor_fn(&msg);
+       }
+
+       /* prepare the final rpc call arguments */
        s->deleteuser.in.user_handle   = &s->user_handle;
        s->deleteuser.out.user_handle  = &s->user_handle;
        
-       s->req = dcerpc_samr_DeleteUser_send(s->pipe, c, &s->deleteuser);
-       
-       s->req->async.callback = userdel_handler;
-       s->req->async.private  = c;
-       s->stage = USERDEL_DELETE;
-       
-       return NT_STATUS_OK;
+       /* send rpc request */
+       deluser_req = dcerpc_samr_DeleteUser_send(s->pipe, c, &s->deleteuser);
+       if (composite_nomem(deluser_req, c)) return;
+
+       /* callback handler setup */
+       composite_continue_rpc(c, deluser_req, continue_userdel_deleted, c);
 }
 
 
 /**
  * Stage 3: Delete user account
  */
-static NTSTATUS userdel_delete(struct composite_context *c,
-                              struct userdel_state *s)
+static void continue_userdel_deleted(struct rpc_request *req)
 {
-       c->status = dcerpc_ndr_request_recv(s->req);
-       NT_STATUS_NOT_OK_RETURN(c->status);
-       
-       c->state = SMBCLI_REQUEST_DONE;
-
-       return NT_STATUS_OK;
-}
+       struct composite_context *c;
+       struct userdel_state *s;
+       struct monitor_msg msg;
 
+       c = talloc_get_type(req->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct userdel_state);
 
-/**
- * Event handler for asynchronous request. Handles transition through
- * intermediate stages of the call.
- *
- * @param req rpc call context
- */
-static void userdel_handler(struct rpc_request *req)
-{
-       struct composite_context *c = req->async.private;
-       struct userdel_state *s = talloc_get_type(c->private, struct userdel_state);
-       struct monitor_msg msg;
-       struct msg_rpc_lookup_name *msg_lookup;
-       struct msg_rpc_open_user *msg_open;
-       
-       switch (s->stage) {
-       case USERDEL_LOOKUP:
-               c->status = userdel_lookup(c, s);
-
-               msg.type = rpc_lookup_name;
-               msg_lookup = talloc(s, struct msg_rpc_lookup_name);
-               msg_lookup->rid = s->lookupname.out.rids.ids;
-               msg_lookup->count = s->lookupname.out.rids.count;
-               msg.data = (void*)msg_lookup;
-               msg.data_size = sizeof(*msg_lookup);
-               break;
-
-       case USERDEL_OPEN:
-               c->status = userdel_open(c, s);
-
-               msg.type = rpc_open_user;
-               msg_open = talloc(s, struct msg_rpc_open_user);
-               msg_open->rid = s->openuser.in.rid;
-               msg_open->access_mask = s->openuser.in.rid;
-               msg.data = (void*)msg_open;
-               msg.data_size = sizeof(*msg_open);
-               break;
-
-       case USERDEL_DELETE:
-               c->status = userdel_delete(c, s);
-               
-               msg.type = rpc_delete_user;
-               msg.data = NULL;
-               msg.data_size = 0;
-               break;
-       }
+       /* receive samr_DeleteUser result */
+       c->status = dcerpc_ndr_request_recv(req);
+       if (!composite_is_ok(c)) return;
 
+       /* return the actual function call status */
+       c->status = s->deleteuser.out.result;
        if (!NT_STATUS_IS_OK(c->status)) {
-               c->state = SMBCLI_REQUEST_ERROR;
+               composite_error(c, c->status);
+               return;
        }
-
-       if (c->monitor_fn) {
-               c->monitor_fn(&msg);
+       
+       /* issue a monitor message */
+       if (s->monitor_fn) {
+               msg.type      = mon_SamrDeleteUser;
+               msg.data      = NULL;
+               msg.data_size = 0;
+               s->monitor_fn(&msg);
        }
 
-       if (c->state >= SMBCLI_REQUEST_DONE &&
-           c->async.fn) {
-               c->async.fn(c);
-       }
+       composite_done(c);
 }
 
 
@@ -359,46 +363,48 @@ static void userdel_handler(struct rpc_request *req)
  *
  * @param p dce/rpc call pipe
  * @param io arguments and results of the call
+ * @param monitor monitor function for providing information about the progress
  */
 
 struct composite_context *libnet_rpc_userdel_send(struct dcerpc_pipe *p,
-                                                 struct libnet_rpc_userdel *io)
+                                                 struct libnet_rpc_userdel *io,
+                                                 void (*monitor)(struct monitor_msg*))
 {
        struct composite_context *c;
        struct userdel_state *s;
-       
-       c = talloc_zero(p, struct composite_context);
-       if (c == NULL) goto failure;
+       struct rpc_request *lookup_req;
+
+       /* composite context allocation and setup */
+       c = composite_create(p, dcerpc_event_context(p));
+       if (c == NULL) return NULL;
 
        s = talloc_zero(c, struct userdel_state);
-       if (s == NULL) goto failure;
+       if (composite_nomem(s, c)) return c;
 
-       c->state      = SMBCLI_REQUEST_SEND;
-       c->private    = s;
-       c->event_ctx  = dcerpc_event_context(p);
+       c->private_data  = s;
 
+       /* store function parameters in the state structure */
        s->pipe          = p;
        s->domain_handle = io->in.domain_handle;
+       s->monitor_fn    = monitor;
        
        /* preparing parameters to send rpc request */
        s->lookupname.in.domain_handle = &io->in.domain_handle;
        s->lookupname.in.num_names     = 1;
        s->lookupname.in.names         = talloc_zero(s, struct lsa_String);
        s->lookupname.in.names->string = io->in.username;
+       s->lookupname.out.rids         = talloc_zero(s, struct samr_Ids);
+       s->lookupname.out.types        = talloc_zero(s, struct samr_Ids);
+       if (composite_nomem(s->lookupname.out.rids, c)) return c;
+       if (composite_nomem(s->lookupname.out.types, c)) return c;
 
        /* send the request */
-       s->req = dcerpc_samr_LookupNames_send(p, c, &s->lookupname);
-
-       /* callback handler */
-       s->req->async.callback = userdel_handler;
-       s->req->async.private  = c;
-       s->stage = USERDEL_LOOKUP;
+       lookup_req = dcerpc_samr_LookupNames_send(p, c, &s->lookupname);
+       if (composite_nomem(lookup_req, c)) return c;
 
+       /* set the next stage */
+       composite_continue_rpc(c, lookup_req, continue_userdel_name_found, c);
        return c;
-
-failure:
-       talloc_free(c);
-       return NULL;
 }
 
 
@@ -420,7 +426,7 @@ NTSTATUS libnet_rpc_userdel_recv(struct composite_context *c, TALLOC_CTX *mem_ct
        status = composite_wait(c);
 
        if (NT_STATUS_IS_OK(status) && io) {
-               s  = talloc_get_type(c->private, struct userdel_state);
+               s  = talloc_get_type(c->private_data, struct userdel_state);
                io->out.user_handle = s->user_handle;
        }
 
@@ -438,23 +444,27 @@ NTSTATUS libnet_rpc_userdel_recv(struct composite_context *c, TALLOC_CTX *mem_ct
  * @return nt status code of execution
  */
 
-NTSTATUS libnet_rpc_userdel(struct dcerpc_pipe *pipe,
+NTSTATUS libnet_rpc_userdel(struct dcerpc_pipe *p,
                            TALLOC_CTX *mem_ctx,
                            struct libnet_rpc_userdel *io)
 {
-       struct composite_context *c = libnet_rpc_userdel_send(pipe, io);
+       struct composite_context *c = libnet_rpc_userdel_send(p, io, NULL);
        return libnet_rpc_userdel_recv(c, mem_ctx, io);
 }
 
 
-static void usermod_handler(struct rpc_request*);
+/*
+ * USER MODIFY functionality
+ */
+
+static void continue_usermod_name_found(struct rpc_request *req);
+static void continue_usermod_user_opened(struct rpc_request *req);
+static void continue_usermod_user_queried(struct rpc_request *req);
+static void continue_usermod_user_changed(struct rpc_request *req);
 
-enum usermod_stage { USERMOD_LOOKUP, USERMOD_OPEN, USERMOD_QUERY, USERMOD_MODIFY };
 
 struct usermod_state {
-       enum usermod_stage         stage;
        struct dcerpc_pipe         *pipe;
-       struct rpc_request         *req;
        struct policy_handle       domain_handle;
        struct policy_handle       user_handle;
        struct usermod_change      change;
@@ -463,263 +473,335 @@ struct usermod_state {
        struct samr_OpenUser       openuser;
        struct samr_SetUserInfo    setuser;
        struct samr_QueryUserInfo  queryuser;
+
+       /* information about the progress */
+       void (*monitor_fn)(struct monitor_msg *);
 };
 
 
 /**
  * Step 1: Lookup user name
  */
-static NTSTATUS usermod_lookup(struct composite_context *c,
-                              struct usermod_state *s)
+static void continue_usermod_name_found(struct rpc_request *req)
 {
-       NTSTATUS status;
+       struct composite_context *c;
+       struct usermod_state *s;
+       struct rpc_request *openuser_req;
+       struct monitor_msg msg;
 
-       c->status = dcerpc_ndr_request_recv(s->req);
-       NT_STATUS_NOT_OK_RETURN(c->status);
+       c = talloc_get_type(req->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct usermod_state);
+
+       /* receive samr_LookupNames result */
+       c->status = dcerpc_ndr_request_recv(req);
+       if (!composite_is_ok(c)) return;
+
+       c->status = s->lookupname.out.result;
+       if (!NT_STATUS_IS_OK(c->status)) {
+               composite_error(c, c->status);
+               return;
+       }
 
-       if (!s->lookupname.out.rids.count) {
-               /* TODO: no such user */
-               status = NT_STATUS_NO_SUCH_USER;
+       /* what to do when there's no user account to delete
+          and what if there's more than one rid resolved */
+       if (!s->lookupname.out.rids->count) {
+               c->status = NT_STATUS_NO_SUCH_USER;
+               composite_error(c, c->status);
+               return;
+
+       } else if (!s->lookupname.out.rids->count > 1) {
+               c->status = NT_STATUS_INVALID_ACCOUNT_NAME;
+               composite_error(c, c->status);
+               return;
+       }
+
+       /* issue a monitor message */
+       if (s->monitor_fn) {
+               struct msg_rpc_lookup_name msg_lookup;
 
-       } else if (!s->lookupname.out.rids.count > 1) {
-               /* TODO: ambiguous username */
-               status = NT_STATUS_INVALID_ACCOUNT_NAME;
+               msg_lookup.rid   = s->lookupname.out.rids->ids;
+               msg_lookup.count = s->lookupname.out.rids->count;
+
+               msg.type      = mon_SamrLookupName;
+               msg.data      = (void*)&msg_lookup;
+               msg.data_size = sizeof(msg_lookup);
+               s->monitor_fn(&msg);
        }
 
+       /* prepare the next rpc call */
        s->openuser.in.domain_handle = &s->domain_handle;
-       s->openuser.in.rid           = s->lookupname.out.rids.ids[0];
+       s->openuser.in.rid           = s->lookupname.out.rids->ids[0];
        s->openuser.in.access_mask   = SEC_FLAG_MAXIMUM_ALLOWED;
        s->openuser.out.user_handle  = &s->user_handle;
 
-       s->req = dcerpc_samr_OpenUser_send(s->pipe, c, &s->openuser);
+       /* send the rpc request */
+       openuser_req = dcerpc_samr_OpenUser_send(s->pipe, c, &s->openuser);
+       if (composite_nomem(openuser_req, c)) return;
 
-       s->req->async.callback = usermod_handler;
-       s->req->async.private  = c;
-       s->stage = USERMOD_OPEN;
-       
-       return NT_STATUS_OK;
+       composite_continue_rpc(c, openuser_req, continue_usermod_user_opened, c);
 }
 
 
-static uint32_t usermod_setfields(struct usermod_state *s, uint16_t *level,
-                                 union samr_UserInfo *i)
+/**
+ * Choose a proper level of samr_UserInfo structure depending on required
+ * change specified by means of flags field. Subsequent calls of this
+ * function are made until there's no flags set meaning that all of the
+ * changes have been made.
+ */
+static bool usermod_setfields(struct usermod_state *s, uint16_t *level,
+                             union samr_UserInfo *i, bool queried)
 {
-       if (s->change.fields) {
-               if (s->change.fields & USERMOD_FIELD_ACCOUNT_NAME) {
-                       *level = 7;
-                       i->info7.account_name.string = s->change.account_name;
+       if (s->change.fields == 0) return s->change.fields;
 
-                       s->change.fields ^= USERMOD_FIELD_ACCOUNT_NAME;
+       *level = 0;
 
-               } else if (s->change.fields & USERMOD_FIELD_FULL_NAME) {
-                       *level = 8;
-                       i->info8.full_name.string = s->change.full_name;
-                       
-                       s->change.fields ^= USERMOD_FIELD_FULL_NAME;
-
-               } else if (s->change.fields & USERMOD_FIELD_DESCRIPTION) {
-                       *level = 13;
-                       i->info13.description.string = s->change.description;
-                       
-                       s->change.fields ^= USERMOD_FIELD_DESCRIPTION;
-
-               } else if (s->change.fields & USERMOD_FIELD_COMMENT) {
-                       *level = 2;
-
-                       if (s->stage == USERMOD_QUERY) {
-                               /* the user info is obtained, so now set the required field */
-                               i->info2.comment.string = s->change.comment;
-                               s->change.fields ^= USERMOD_FIELD_COMMENT;
+       if ((s->change.fields & USERMOD_FIELD_ACCOUNT_NAME) &&
+           (*level == 0 || *level == 7)) {
+               *level = 7;
+               i->info7.account_name.string = s->change.account_name;
+               
+               s->change.fields ^= USERMOD_FIELD_ACCOUNT_NAME;
+       }
 
-                       } else {
-                               /* we need to query the user info before setting one field in it */
-                               s->stage = USERMOD_QUERY;
-                               return s->change.fields;
-                       }
+       if ((s->change.fields & USERMOD_FIELD_FULL_NAME) &&
+           (*level == 0 || *level == 8)) {
+               *level = 8;
+               i->info8.full_name.string = s->change.full_name;
+               
+               s->change.fields ^= USERMOD_FIELD_FULL_NAME;
+       }
+       
+       if ((s->change.fields & USERMOD_FIELD_DESCRIPTION) &&
+           (*level == 0 || *level == 13)) {
+               *level = 13;
+               i->info13.description.string = s->change.description;
+               
+               s->change.fields ^= USERMOD_FIELD_DESCRIPTION;          
+       }
 
-               } else if (s->change.fields & USERMOD_FIELD_ALLOW_PASS_CHG) {
-                       *level = 3;
-                       
-                       if (s->stage == USERMOD_QUERY) {
-                               i->info3.allow_password_change = timeval_to_nttime(s->change.allow_password_change);
-                               s->change.fields ^= USERMOD_FIELD_ALLOW_PASS_CHG;
-
-                       } else {
-                               s->stage = USERMOD_QUERY;
-                               return s->change.fields;
-                       }
-
-               } else if (s->change.fields & USERMOD_FIELD_FORCE_PASS_CHG) {
-                       *level = 3;
-
-                       if (s->stage == USERMOD_QUERY) {
-                               i->info3.force_password_change = timeval_to_nttime(s->change.force_password_change);
-                               s->change.fields ^= USERMOD_FIELD_FORCE_PASS_CHG;
-
-                       } else {
-                               s->stage = USERMOD_QUERY;
-                               return s->change.fields;
-                       }
-
-               } else if (s->change.fields & USERMOD_FIELD_LOGON_SCRIPT) {
-                       *level = 11;
-                       i->info11.logon_script.string = s->change.logon_script;
+       if ((s->change.fields & USERMOD_FIELD_COMMENT) &&
+           (*level == 0 || *level == 2)) {
+               *level = 2;
+               
+               if (queried) {
+                       /* the user info is obtained, so now set the required field */
+                       i->info2.comment.string = s->change.comment;
+                       s->change.fields ^= USERMOD_FIELD_COMMENT;
                        
-                       s->change.fields ^= USERMOD_FIELD_LOGON_SCRIPT;
-
-               } else if (s->change.fields & USERMOD_FIELD_PROFILE_PATH) {
-                       *level = 12;
-                       i->info12.profile_path.string = s->change.profile_path;
-
-                       s->change.fields ^= USERMOD_FIELD_PROFILE_PATH;
+               } else {
+                       /* we need to query the user info before setting one field in it */
+                       return false;
+               }
+       }
 
-               } else if (s->change.fields & USERMOD_FIELD_ACCT_EXPIRY) {
-                       *level = 17;
-                       i->info17.acct_expiry = timeval_to_nttime(s->change.acct_expiry);
+       if ((s->change.fields & USERMOD_FIELD_LOGON_SCRIPT) &&
+           (*level == 0 || *level == 11)) {
+               *level = 11;
+               i->info11.logon_script.string = s->change.logon_script;
+               
+               s->change.fields ^= USERMOD_FIELD_LOGON_SCRIPT;
+       }
 
-                       s->change.fields ^= USERMOD_FIELD_ACCT_EXPIRY;
+       if ((s->change.fields & USERMOD_FIELD_PROFILE_PATH) &&
+           (*level == 0 || *level == 12)) {
+               *level = 12;
+               i->info12.profile_path.string = s->change.profile_path;
+               
+               s->change.fields ^= USERMOD_FIELD_PROFILE_PATH;
+       }
 
-               } else if (s->change.fields & USERMOD_FIELD_ACCT_FLAGS) {
-                       *level = 16;
-                       i->info16.acct_flags = s->change.acct_flags;
+       if ((s->change.fields & USERMOD_FIELD_HOME_DIRECTORY) &&
+           (*level == 0 || *level == 10)) {
+               *level = 10;
+               
+               if (queried) {
+                       i->info10.home_directory.string = s->change.home_directory;
+                       s->change.fields ^= USERMOD_FIELD_HOME_DIRECTORY;
+               } else {
+                       return false;
+               }
+       }
 
-                       s->change.fields ^= USERMOD_FIELD_ACCT_FLAGS;
+       if ((s->change.fields & USERMOD_FIELD_HOME_DRIVE) &&
+           (*level == 0 || *level == 10)) {
+               *level = 10;
+               
+               if (queried) {
+                       i->info10.home_drive.string = s->change.home_drive;
+                       s->change.fields ^= USERMOD_FIELD_HOME_DRIVE;
+               } else {
+                       return false;
                }
        }
+       
+       if ((s->change.fields & USERMOD_FIELD_ACCT_EXPIRY) &&
+           (*level == 0 || *level == 17)) {
+               *level = 17;
+               i->info17.acct_expiry = timeval_to_nttime(s->change.acct_expiry);
+               
+               s->change.fields ^= USERMOD_FIELD_ACCT_EXPIRY;
+       }
 
-       /* We're going to be back here again soon unless all fields have been set */
-       if (s->change.fields) {
-               s->stage = USERMOD_OPEN;
-       } else {
-               s->stage = USERMOD_MODIFY;
+       if ((s->change.fields & USERMOD_FIELD_ACCT_FLAGS) &&
+           (*level == 0 || *level == 16)) {
+               *level = 16;
+               i->info16.acct_flags = s->change.acct_flags;
+               
+               s->change.fields ^= USERMOD_FIELD_ACCT_FLAGS;
        }
 
-       return s->change.fields;
+       /* We're going to be here back again soon unless all fields have been set */
+       return true;
 }
 
 
-/**
- * Stage 2: Open user account
- */
-static NTSTATUS usermod_open(struct composite_context *c,
-                            struct usermod_state *s)
+static NTSTATUS usermod_change(struct composite_context *c,
+                              struct usermod_state *s)
 {
+       struct rpc_request *query_req, *setuser_req;
+       bool do_set;
        union samr_UserInfo *i = &s->info;
-       uint16_t level;
 
-       c->status = dcerpc_ndr_request_recv(s->req);
-       NT_STATUS_NOT_OK_RETURN(c->status);
+       /* set the level to invalid value, so that unless setfields routine 
+          gives it a valid value we report the error correctly */
+       uint16_t level = 27;
 
-       /* Prepare UserInfo level and data based on bitmask field */
-       s->change.fields = usermod_setfields(s, &level, i);
+       /* prepare UserInfo level and data based on bitmask field */
+       do_set = usermod_setfields(s, &level, i, false);
+
+       if (level < 1 || level > 26) {
+               /* apparently there's a field that the setfields routine
+                  does not know how to set */
+               return NT_STATUS_INVALID_PARAMETER;
+       }
 
-       if (s->stage == USERMOD_QUERY) {
+       /* If some specific level is used to set user account data and the change
+          itself does not cover all fields then we need to query the user info
+          first, right before changing the data. Otherwise we could set required
+          fields and accidentally reset the others.
+       */
+       if (!do_set) {
                s->queryuser.in.user_handle = &s->user_handle;
                s->queryuser.in.level       = level;
+               s->queryuser.out.info       = talloc(s, union samr_UserInfo *);
+               if (composite_nomem(s->queryuser.out.info, c)) return;
 
-               s->req = dcerpc_samr_QueryUserInfo_send(s->pipe, c, &s->queryuser);
+
+               /* send query user info request to retrieve complete data of
+                  a particular info level */
+               query_req = dcerpc_samr_QueryUserInfo_send(s->pipe, c, &s->queryuser);
+               composite_continue_rpc(c, query_req, continue_usermod_user_queried, c);
 
        } else {
                s->setuser.in.user_handle  = &s->user_handle;
                s->setuser.in.level        = level;
                s->setuser.in.info         = i;
 
-               s->req = dcerpc_samr_SetUserInfo_send(s->pipe, c, &s->setuser);
+               /* send set user info request after making required change */
+               setuser_req = dcerpc_samr_SetUserInfo_send(s->pipe, c, &s->setuser);
+               composite_continue_rpc(c, setuser_req, continue_usermod_user_changed, c);
        }
-
-       s->req->async.callback = usermod_handler;
-       s->req->async.private  = c;
-
+       
        return NT_STATUS_OK;
 }
 
 
 /**
- * Stage 2a (optional): Query the user information
+ * Stage 2: Open user account
  */
-static NTSTATUS usermod_query(struct composite_context *c,
-                             struct usermod_state *s)
+static void continue_usermod_user_opened(struct rpc_request *req)
 {
-       union samr_UserInfo *i = &s->info;
-       uint16_t level;
-
-       c->status = dcerpc_ndr_request_recv(s->req);
-       NT_STATUS_NOT_OK_RETURN(c->status);
-
-       s->info = *s->queryuser.out.info;
+       struct composite_context *c;
+       struct usermod_state *s;
 
-       s->change.fields = usermod_setfields(s, &level, i);
+       c = talloc_get_type(req->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct usermod_state);
 
-       s->setuser.in.user_handle  = &s->user_handle;
-       s->setuser.in.level        = level;
-       s->setuser.in.info         = i;
-       
-       s->req = dcerpc_samr_SetUserInfo_send(s->pipe, c, &s->setuser);
+       c->status = dcerpc_ndr_request_recv(req);
+       if (!composite_is_ok(c)) return;
 
-       s->req->async.callback = usermod_handler;
-       s->req->async.private  = c;
+       c->status = s->openuser.out.result;
+       if (!NT_STATUS_IS_OK(c->status)) {
+               composite_error(c, c->status);
+               return;
+       }
 
-       return NT_STATUS_OK;
+       c->status = usermod_change(c, s);
 }
 
 
 /**
- * Stage 3: Set new user account data
+ * Stage 2a (optional): Query the user information
  */
-static NTSTATUS usermod_modify(struct composite_context *c,
-                              struct usermod_state *s)
+static void continue_usermod_user_queried(struct rpc_request *req)
 {
-       c->status = dcerpc_ndr_request_recv(s->req);
-       NT_STATUS_NOT_OK_RETURN(c->status);
+       struct composite_context *c;
+       struct usermod_state *s;
+       union samr_UserInfo *i;
+       uint16_t level;
+       struct rpc_request *setuser_req;
+       
+       c = talloc_get_type(req->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct usermod_state);
 
-       c->state = SMBCLI_REQUEST_DONE;
+       i = &s->info;
 
-       return NT_STATUS_OK;
-}
+       /* receive samr_QueryUserInfo result */
+       c->status = dcerpc_ndr_request_recv(req);
+       if (!composite_is_ok(c)) return;
+
+       c->status = s->queryuser.out.result;
+       if (!NT_STATUS_IS_OK(c->status)) {
+               composite_error(c, c->status);
+               return;
+       }
 
+       /* get returned user data and make a change (potentially one
+          of many) */
+       s->info = *(*s->queryuser.out.info);
 
-/**
- * Event handler for asynchronous request. Handles transition through
- * intermediate stages of the call.
- *
- * @param req rpc call context
- */
+       usermod_setfields(s, &level, i, true);
 
-static void usermod_handler(struct rpc_request *req)
-{
-       struct composite_context *c = req->async.private;
-       struct usermod_state *s = talloc_get_type(c->private, struct usermod_state);
-       struct monitor_msg msg;
+       /* prepare rpc call arguments */
+       s->setuser.in.user_handle  = &s->user_handle;
+       s->setuser.in.level        = level;
+       s->setuser.in.info         = i;
 
-       switch (s->stage) {
-       case USERMOD_LOOKUP:
-               c->status = usermod_lookup(c, s);
-               break;
+       /* send the rpc request */
+       setuser_req = dcerpc_samr_SetUserInfo_send(s->pipe, c, &s->setuser);
+       composite_continue_rpc(c, setuser_req, continue_usermod_user_changed, c);
+}
 
-       case USERMOD_OPEN:
-               c->status = usermod_open(c, s);
-               break;
 
-       case USERMOD_QUERY:
-               c->status = usermod_query(c, s);
-               break;
+/**
+ * Stage 3: Set new user account data
+ */
+static void continue_usermod_user_changed(struct rpc_request *req)
+{
+       struct composite_context *c;
+       struct usermod_state *s;
+       
+       c = talloc_get_type(req->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct usermod_state);
 
-       case USERMOD_MODIFY:
-               c->status = usermod_modify(c, s);
-               break;
-       }
+       /* receive samr_SetUserInfo result */
+       c->status = dcerpc_ndr_request_recv(req);
+       if (!composite_is_ok(c)) return;
 
+       /* return the actual function call status */
+       c->status = s->setuser.out.result;
        if (!NT_STATUS_IS_OK(c->status)) {
-               c->state = SMBCLI_REQUEST_ERROR;
+               composite_error(c, c->status);
+               return;
        }
 
-       if (c->monitor_fn) {
-               c->monitor_fn(&msg);
-       }
+       if (s->change.fields == 0) {
+               /* all fields have been set - we're done */
+               composite_done(c);
 
-       if (c->state >= SMBCLI_REQUEST_DONE &&
-           c->async.fn) {
-               c->async.fn(c);
+       } else {
+               /* something's still not changed - repeat the procedure */
+               c->status = usermod_change(c, s);
        }
 }
 
@@ -729,44 +811,48 @@ static void usermod_handler(struct rpc_request *req)
  *
  * @param p dce/rpc call pipe
  * @param io arguments and results of the call
+ * @param monitor monitor function for providing information about the progress
  */
 
 struct composite_context *libnet_rpc_usermod_send(struct dcerpc_pipe *p,
-                                                 struct libnet_rpc_usermod *io)
+                                                 struct libnet_rpc_usermod *io,
+                                                 void (*monitor)(struct monitor_msg*))
 {
        struct composite_context *c;
        struct usermod_state *s;
-       
-       c = talloc_zero(p, struct composite_context);
-       if (c == NULL) goto failure;
+       struct rpc_request *lookup_req;
 
+       /* composite context allocation and setup */
+       c = composite_create(p, dcerpc_event_context(p));
+       if (c == NULL) return NULL;
        s = talloc_zero(c, struct usermod_state);
-       if (s == NULL) goto failure;
+       if (composite_nomem(s, c)) return c;
 
-       c->state      = SMBCLI_REQUEST_SEND;
-       c->private    = s;
-       c->event_ctx  = dcerpc_event_context(p);
+       c->private_data = s;
 
+       /* store parameters in the call structure */
        s->pipe          = p;
        s->domain_handle = io->in.domain_handle;
        s->change        = io->in.change;
+       s->monitor_fn    = monitor;
        
+       /* prepare rpc call arguments */
        s->lookupname.in.domain_handle = &io->in.domain_handle;
        s->lookupname.in.num_names     = 1;
        s->lookupname.in.names         = talloc_zero(s, struct lsa_String);
        s->lookupname.in.names->string = io->in.username;
+       s->lookupname.out.rids         = talloc_zero(s, struct samr_Ids);
+       s->lookupname.out.types        = talloc_zero(s, struct samr_Ids);
+       if (composite_nomem(s->lookupname.out.rids, c)) return c;
+       if (composite_nomem(s->lookupname.out.types, c)) return c;
+
+       /* send the rpc request */
+       lookup_req = dcerpc_samr_LookupNames_send(p, c, &s->lookupname);
+       if (composite_nomem(lookup_req, c)) return c;
        
-       s->req = dcerpc_samr_LookupNames_send(p, c, &s->lookupname);
-       
-       s->req->async.callback = usermod_handler;
-       s->req->async.private  = c;
-       s->stage = USERMOD_LOOKUP;
-
+       /* callback handler setup */
+       composite_continue_rpc(c, lookup_req, continue_usermod_name_found, c);
        return c;
-
-failure:
-       talloc_free(c);
-       return NULL;
 }
 
 
@@ -783,7 +869,6 @@ NTSTATUS libnet_rpc_usermod_recv(struct composite_context *c, TALLOC_CTX *mem_ct
                                 struct libnet_rpc_usermod *io)
 {
        NTSTATUS status;
-       struct usermod_state *s;
        
        status = composite_wait(c);
 
@@ -801,10 +886,10 @@ NTSTATUS libnet_rpc_usermod_recv(struct composite_context *c, TALLOC_CTX *mem_ct
  * @return nt status code of execution
  */
 
-NTSTATUS libnet_rpc_usermod(struct dcerpc_pipe *pipe,
+NTSTATUS libnet_rpc_usermod(struct dcerpc_pipe *p,
                            TALLOC_CTX *mem_ctx,
                            struct libnet_rpc_usermod *io)
 {
-       struct composite_context *c = libnet_rpc_usermod_send(pipe, io);
+       struct composite_context *c = libnet_rpc_usermod_send(p, io, NULL);
        return libnet_rpc_usermod_recv(c, mem_ctx, io);
 }