Merge branch 'selftest' of git://git.samba.org/jelmer/samba
[kai/samba.git] / source4 / libnet / userman.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Rafal Szczesniak 2005
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21   a composite functions for user management operations (add/del/chg)
22 */
23
24 #include "includes.h"
25 #include "libcli/composite/composite.h"
26 #include "libnet/composite.h"
27 #include "libnet/userman.h"
28 #include "libnet/userinfo.h"
29 #include "librpc/gen_ndr/ndr_samr_c.h"
30 #include "libnet/libnet_proto.h"
31
32 /*
33  * Composite USER ADD functionality
34  */
35
36 struct useradd_state {
37         struct dcerpc_pipe       *pipe;
38         struct rpc_request       *req;
39         struct policy_handle     domain_handle;
40         struct samr_CreateUser   createuser;
41         struct policy_handle     user_handle;
42         uint32_t                 user_rid;
43
44         /* information about the progress */
45         void (*monitor_fn)(struct monitor_msg *);
46 };
47
48
49 static void continue_useradd_create(struct rpc_request *req);
50
51
52 /**
53  * Stage 1 (and the only one for now): Create user account.
54  */
55 static void continue_useradd_create(struct rpc_request *req)
56 {
57         struct composite_context *c;
58         struct useradd_state *s;
59
60         c = talloc_get_type(req->async.private_data, struct composite_context);
61         s = talloc_get_type(c->private_data, struct useradd_state);
62
63         /* check rpc layer status code */
64         c->status = dcerpc_ndr_request_recv(s->req);
65         if (!composite_is_ok(c)) return;
66
67         /* check create user call status code */
68         c->status = s->createuser.out.result;
69
70         /* get created user account data */
71         s->user_handle = *s->createuser.out.user_handle;
72         s->user_rid    = *s->createuser.out.rid;
73
74         /* issue a monitor message */
75         if (s->monitor_fn) {
76                 struct monitor_msg msg;
77                 struct msg_rpc_create_user rpc_create;
78
79                 rpc_create.rid = *s->createuser.out.rid;
80
81                 msg.type      = mon_SamrCreateUser;
82                 msg.data      = (void*)&rpc_create;
83                 msg.data_size = sizeof(rpc_create);
84                 
85                 s->monitor_fn(&msg);
86         }
87         
88         composite_done(c);
89 }
90
91
92 /**
93  * Sends asynchronous useradd request
94  *
95  * @param p dce/rpc call pipe 
96  * @param io arguments and results of the call
97  * @param monitor monitor function for providing information about the progress
98  */
99
100 struct composite_context *libnet_rpc_useradd_send(struct dcerpc_pipe *p,
101                                                   struct libnet_rpc_useradd *io,
102                                                   void (*monitor)(struct monitor_msg*))
103 {
104         struct composite_context *c;
105         struct useradd_state *s;
106
107         if (!p || !io) return NULL;
108
109         /* composite allocation and setup */
110         c = composite_create(p, dcerpc_event_context(p));
111         if (c == NULL) return NULL;
112         
113         s = talloc_zero(c, struct useradd_state);
114         if (composite_nomem(s, c)) return c;
115         
116         c->private_data = s;
117
118         /* put passed arguments to the state structure */
119         s->domain_handle = io->in.domain_handle;
120         s->pipe          = p;
121         s->monitor_fn    = monitor;
122         
123         /* preparing parameters to send rpc request */
124         s->createuser.in.domain_handle         = &io->in.domain_handle;
125
126         s->createuser.in.account_name          = talloc_zero(c, struct lsa_String);
127         if (composite_nomem(s->createuser.in.account_name, c)) return c;
128
129         s->createuser.in.account_name->string  = talloc_strdup(c, io->in.username);
130         if (composite_nomem(s->createuser.in.account_name->string, c)) return c;
131
132         s->createuser.out.user_handle          = &s->user_handle;
133         s->createuser.out.rid                  = &s->user_rid;
134
135         /* send the request */
136         s->req = dcerpc_samr_CreateUser_send(p, c, &s->createuser);
137         if (composite_nomem(s->req, c)) return c;
138
139         composite_continue_rpc(c, s->req, continue_useradd_create, c);
140         return c;
141 }
142
143
144 /**
145  * Waits for and receives result of asynchronous useradd call
146  * 
147  * @param c composite context returned by asynchronous useradd call
148  * @param mem_ctx memory context of the call
149  * @param io pointer to results (and arguments) of the call
150  * @return nt status code of execution
151  */
152
153 NTSTATUS libnet_rpc_useradd_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
154                                  struct libnet_rpc_useradd *io)
155 {
156         NTSTATUS status;
157         struct useradd_state *s;
158         
159         status = composite_wait(c);
160         
161         if (NT_STATUS_IS_OK(status) && io) {
162                 /* get and return result of the call */
163                 s = talloc_get_type(c->private_data, struct useradd_state);
164                 io->out.user_handle = s->user_handle;
165         }
166
167         talloc_free(c);
168         return status;
169 }
170
171
172 /**
173  * Synchronous version of useradd call
174  *
175  * @param pipe dce/rpc call pipe
176  * @param mem_ctx memory context for the call
177  * @param io arguments and results of the call
178  * @return nt status code of execution
179  */
180
181 NTSTATUS libnet_rpc_useradd(struct dcerpc_pipe *p,
182                             TALLOC_CTX *mem_ctx,
183                             struct libnet_rpc_useradd *io)
184 {
185         struct composite_context *c = libnet_rpc_useradd_send(p, io, NULL);
186         return libnet_rpc_useradd_recv(c, mem_ctx, io);
187 }
188
189
190
191 /*
192  * Composite USER DELETE functionality
193  */
194
195
196 struct userdel_state {
197         struct dcerpc_pipe        *pipe;
198         struct policy_handle      domain_handle;
199         struct policy_handle      user_handle;
200         struct samr_LookupNames   lookupname;
201         struct samr_OpenUser      openuser;
202         struct samr_DeleteUser    deleteuser;
203
204         /* information about the progress */
205         void (*monitor_fn)(struct monitor_msg *);
206 };
207
208
209 static void continue_userdel_name_found(struct rpc_request *req);
210 static void continue_userdel_user_opened(struct rpc_request* req);
211 static void continue_userdel_deleted(struct rpc_request *req);
212
213
214 /**
215  * Stage 1: Lookup the user name and resolve it to rid
216  */
217 static void continue_userdel_name_found(struct rpc_request *req)
218 {
219         struct composite_context *c;
220         struct userdel_state *s;
221         struct rpc_request *openuser_req;
222         struct monitor_msg msg;
223
224         c = talloc_get_type(req->async.private_data, struct composite_context);
225         s = talloc_get_type(c->private_data, struct userdel_state);
226
227         /* receive samr_LookupNames result */
228         c->status = dcerpc_ndr_request_recv(req);
229         if (!composite_is_ok(c)) return;
230
231         c->status = s->lookupname.out.result;
232         if (!NT_STATUS_IS_OK(c->status)) {
233                 composite_error(c, c->status);
234                 return;
235         }
236
237         /* what to do when there's no user account to delete
238            and what if there's more than one rid resolved */
239         if (!s->lookupname.out.rids.count) {
240                 c->status = NT_STATUS_NO_SUCH_USER;
241                 composite_error(c, c->status);
242                 return;
243
244         } else if (!s->lookupname.out.rids.count > 1) {
245                 c->status = NT_STATUS_INVALID_ACCOUNT_NAME;
246                 composite_error(c, c->status);
247                 return;
248         }
249
250         /* issue a monitor message */
251         if (s->monitor_fn) {
252                 struct msg_rpc_lookup_name msg_lookup;
253
254                 msg_lookup.rid   = s->lookupname.out.rids.ids;
255                 msg_lookup.count = s->lookupname.out.rids.count;
256
257                 msg.type      = mon_SamrLookupName;
258                 msg.data      = (void*)&msg_lookup;
259                 msg.data_size = sizeof(msg_lookup);
260                 s->monitor_fn(&msg);
261         }
262
263         /* prepare the arguments for rpc call */
264         s->openuser.in.domain_handle = &s->domain_handle;
265         s->openuser.in.rid           = s->lookupname.out.rids.ids[0];
266         s->openuser.in.access_mask   = SEC_FLAG_MAXIMUM_ALLOWED;
267         s->openuser.out.user_handle  = &s->user_handle;
268
269         /* send rpc request */
270         openuser_req = dcerpc_samr_OpenUser_send(s->pipe, c, &s->openuser);
271         if (composite_nomem(openuser_req, c)) return;
272
273         composite_continue_rpc(c, openuser_req, continue_userdel_user_opened, c);
274 }
275
276
277 /**
278  * Stage 2: Open user account.
279  */
280 static void continue_userdel_user_opened(struct rpc_request* req)
281 {
282         struct composite_context *c;
283         struct userdel_state *s;
284         struct rpc_request *deluser_req;
285         struct monitor_msg msg;
286
287         c = talloc_get_type(req->async.private_data, struct composite_context);
288         s = talloc_get_type(c->private_data, struct userdel_state);
289
290         /* receive samr_OpenUser result */
291         c->status = dcerpc_ndr_request_recv(req);
292         if (!composite_is_ok(c)) return;
293
294         c->status = s->openuser.out.result;
295         if (!NT_STATUS_IS_OK(c->status)) {
296                 composite_error(c, c->status);
297                 return;
298         }
299         
300         /* issue a monitor message */
301         if (s->monitor_fn) {
302                 struct msg_rpc_open_user msg_open;
303
304                 msg_open.rid         = s->openuser.in.rid;
305                 msg_open.access_mask = s->openuser.in.access_mask;
306
307                 msg.type      = mon_SamrOpenUser;
308                 msg.data      = (void*)&msg_open;
309                 msg.data_size = sizeof(msg_open);
310                 s->monitor_fn(&msg);
311         }
312
313         /* prepare the final rpc call arguments */
314         s->deleteuser.in.user_handle   = &s->user_handle;
315         s->deleteuser.out.user_handle  = &s->user_handle;
316         
317         /* send rpc request */
318         deluser_req = dcerpc_samr_DeleteUser_send(s->pipe, c, &s->deleteuser);
319         if (composite_nomem(deluser_req, c)) return;
320
321         /* callback handler setup */
322         composite_continue_rpc(c, deluser_req, continue_userdel_deleted, c);
323 }
324
325
326 /**
327  * Stage 3: Delete user account
328  */
329 static void continue_userdel_deleted(struct rpc_request *req)
330 {
331         struct composite_context *c;
332         struct userdel_state *s;
333         struct monitor_msg msg;
334
335         c = talloc_get_type(req->async.private_data, struct composite_context);
336         s = talloc_get_type(c->private_data, struct userdel_state);
337
338         /* receive samr_DeleteUser result */
339         c->status = dcerpc_ndr_request_recv(req);
340         if (!composite_is_ok(c)) return;
341
342         /* return the actual function call status */
343         c->status = s->deleteuser.out.result;
344         if (!NT_STATUS_IS_OK(c->status)) {
345                 composite_error(c, c->status);
346                 return;
347         }
348         
349         /* issue a monitor message */
350         if (s->monitor_fn) {
351                 msg.type      = mon_SamrDeleteUser;
352                 msg.data      = NULL;
353                 msg.data_size = 0;
354                 s->monitor_fn(&msg);
355         }
356
357         composite_done(c);
358 }
359
360
361 /**
362  * Sends asynchronous userdel request
363  *
364  * @param p dce/rpc call pipe
365  * @param io arguments and results of the call
366  * @param monitor monitor function for providing information about the progress
367  */
368
369 struct composite_context *libnet_rpc_userdel_send(struct dcerpc_pipe *p,
370                                                   struct libnet_rpc_userdel *io,
371                                                   void (*monitor)(struct monitor_msg*))
372 {
373         struct composite_context *c;
374         struct userdel_state *s;
375         struct rpc_request *lookup_req;
376
377         /* composite context allocation and setup */
378         c = composite_create(p, dcerpc_event_context(p));
379         if (c == NULL) return NULL;
380
381         s = talloc_zero(c, struct userdel_state);
382         if (composite_nomem(s, c)) return c;
383
384         c->private_data  = s;
385
386         /* store function parameters in the state structure */
387         s->pipe          = p;
388         s->domain_handle = io->in.domain_handle;
389         s->monitor_fn    = monitor;
390         
391         /* preparing parameters to send rpc request */
392         s->lookupname.in.domain_handle = &io->in.domain_handle;
393         s->lookupname.in.num_names     = 1;
394         s->lookupname.in.names         = talloc_zero(s, struct lsa_String);
395         s->lookupname.in.names->string = io->in.username;
396
397         /* send the request */
398         lookup_req = dcerpc_samr_LookupNames_send(p, c, &s->lookupname);
399         if (composite_nomem(lookup_req, c)) return c;
400
401         /* set the next stage */
402         composite_continue_rpc(c, lookup_req, continue_userdel_name_found, c);
403         return c;
404 }
405
406
407 /**
408  * Waits for and receives results of asynchronous userdel call
409  *
410  * @param c composite context returned by asynchronous userdel call
411  * @param mem_ctx memory context of the call
412  * @param io pointer to results (and arguments) of the call
413  * @return nt status code of execution
414  */
415
416 NTSTATUS libnet_rpc_userdel_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
417                                  struct libnet_rpc_userdel *io)
418 {
419         NTSTATUS status;
420         struct userdel_state *s;
421         
422         status = composite_wait(c);
423
424         if (NT_STATUS_IS_OK(status) && io) {
425                 s  = talloc_get_type(c->private_data, struct userdel_state);
426                 io->out.user_handle = s->user_handle;
427         }
428
429         talloc_free(c);
430         return status;
431 }
432
433
434 /**
435  * Synchronous version of userdel call
436  *
437  * @param pipe dce/rpc call pipe
438  * @param mem_ctx memory context for the call
439  * @param io arguments and results of the call
440  * @return nt status code of execution
441  */
442
443 NTSTATUS libnet_rpc_userdel(struct dcerpc_pipe *p,
444                             TALLOC_CTX *mem_ctx,
445                             struct libnet_rpc_userdel *io)
446 {
447         struct composite_context *c = libnet_rpc_userdel_send(p, io, NULL);
448         return libnet_rpc_userdel_recv(c, mem_ctx, io);
449 }
450
451
452 /*
453  * USER MODIFY functionality
454  */
455
456 static void continue_usermod_name_found(struct rpc_request *req);
457 static void continue_usermod_user_opened(struct rpc_request *req);
458 static void continue_usermod_user_queried(struct rpc_request *req);
459 static void continue_usermod_user_changed(struct rpc_request *req);
460
461
462 struct usermod_state {
463         struct dcerpc_pipe         *pipe;
464         struct policy_handle       domain_handle;
465         struct policy_handle       user_handle;
466         struct usermod_change      change;
467         union  samr_UserInfo       info;
468         struct samr_LookupNames    lookupname;
469         struct samr_OpenUser       openuser;
470         struct samr_SetUserInfo    setuser;
471         struct samr_QueryUserInfo  queryuser;
472
473         /* information about the progress */
474         void (*monitor_fn)(struct monitor_msg *);
475 };
476
477
478 /**
479  * Step 1: Lookup user name
480  */
481 static void continue_usermod_name_found(struct rpc_request *req)
482 {
483         struct composite_context *c;
484         struct usermod_state *s;
485         struct rpc_request *openuser_req;
486         struct monitor_msg msg;
487
488         c = talloc_get_type(req->async.private_data, struct composite_context);
489         s = talloc_get_type(c->private_data, struct usermod_state);
490
491         /* receive samr_LookupNames result */
492         c->status = dcerpc_ndr_request_recv(req);
493         if (!composite_is_ok(c)) return;
494
495         c->status = s->lookupname.out.result;
496         if (!NT_STATUS_IS_OK(c->status)) {
497                 composite_error(c, c->status);
498                 return;
499         }
500
501         /* what to do when there's no user account to delete
502            and what if there's more than one rid resolved */
503         if (!s->lookupname.out.rids.count) {
504                 c->status = NT_STATUS_NO_SUCH_USER;
505                 composite_error(c, c->status);
506                 return;
507
508         } else if (!s->lookupname.out.rids.count > 1) {
509                 c->status = NT_STATUS_INVALID_ACCOUNT_NAME;
510                 composite_error(c, c->status);
511                 return;
512         }
513
514         /* issue a monitor message */
515         if (s->monitor_fn) {
516                 struct msg_rpc_lookup_name msg_lookup;
517
518                 msg_lookup.rid   = s->lookupname.out.rids.ids;
519                 msg_lookup.count = s->lookupname.out.rids.count;
520
521                 msg.type      = mon_SamrLookupName;
522                 msg.data      = (void*)&msg_lookup;
523                 msg.data_size = sizeof(msg_lookup);
524                 s->monitor_fn(&msg);
525         }
526
527         /* prepare the next rpc call */
528         s->openuser.in.domain_handle = &s->domain_handle;
529         s->openuser.in.rid           = s->lookupname.out.rids.ids[0];
530         s->openuser.in.access_mask   = SEC_FLAG_MAXIMUM_ALLOWED;
531         s->openuser.out.user_handle  = &s->user_handle;
532
533         /* send the rpc request */
534         openuser_req = dcerpc_samr_OpenUser_send(s->pipe, c, &s->openuser);
535         if (composite_nomem(openuser_req, c)) return;
536
537         composite_continue_rpc(c, openuser_req, continue_usermod_user_opened, c);
538 }
539
540
541 /**
542  * Choose a proper level of samr_UserInfo structure depending on required
543  * change specified by means of flags field. Subsequent calls of this
544  * function are made until there's no flags set meaning that all of the
545  * changes have been made.
546  */
547 static bool usermod_setfields(struct usermod_state *s, uint16_t *level,
548                               union samr_UserInfo *i, bool queried)
549 {
550         if (s->change.fields == 0) return s->change.fields;
551
552         *level = 0;
553
554         if ((s->change.fields & USERMOD_FIELD_ACCOUNT_NAME) &&
555             (*level == 0 || *level == 7)) {
556                 *level = 7;
557                 i->info7.account_name.string = s->change.account_name;
558                 
559                 s->change.fields ^= USERMOD_FIELD_ACCOUNT_NAME;
560         }
561
562         if ((s->change.fields & USERMOD_FIELD_FULL_NAME) &&
563             (*level == 0 || *level == 8)) {
564                 *level = 8;
565                 i->info8.full_name.string = s->change.full_name;
566                 
567                 s->change.fields ^= USERMOD_FIELD_FULL_NAME;
568         }
569         
570         if ((s->change.fields & USERMOD_FIELD_DESCRIPTION) &&
571             (*level == 0 || *level == 13)) {
572                 *level = 13;
573                 i->info13.description.string = s->change.description;
574                 
575                 s->change.fields ^= USERMOD_FIELD_DESCRIPTION;          
576         }
577
578         if ((s->change.fields & USERMOD_FIELD_COMMENT) &&
579             (*level == 0 || *level == 2)) {
580                 *level = 2;
581                 
582                 if (queried) {
583                         /* the user info is obtained, so now set the required field */
584                         i->info2.comment.string = s->change.comment;
585                         s->change.fields ^= USERMOD_FIELD_COMMENT;
586                         
587                 } else {
588                         /* we need to query the user info before setting one field in it */
589                         return false;
590                 }
591         }
592
593         if ((s->change.fields & USERMOD_FIELD_LOGON_SCRIPT) &&
594             (*level == 0 || *level == 11)) {
595                 *level = 11;
596                 i->info11.logon_script.string = s->change.logon_script;
597                 
598                 s->change.fields ^= USERMOD_FIELD_LOGON_SCRIPT;
599         }
600
601         if ((s->change.fields & USERMOD_FIELD_PROFILE_PATH) &&
602             (*level == 0 || *level == 12)) {
603                 *level = 12;
604                 i->info12.profile_path.string = s->change.profile_path;
605                 
606                 s->change.fields ^= USERMOD_FIELD_PROFILE_PATH;
607         }
608
609         if ((s->change.fields & USERMOD_FIELD_HOME_DIRECTORY) &&
610             (*level == 0 || *level == 10)) {
611                 *level = 10;
612                 
613                 if (queried) {
614                         i->info10.home_directory.string = s->change.home_directory;
615                         s->change.fields ^= USERMOD_FIELD_HOME_DIRECTORY;
616                 } else {
617                         return false;
618                 }
619         }
620
621         if ((s->change.fields & USERMOD_FIELD_HOME_DRIVE) &&
622             (*level == 0 || *level == 10)) {
623                 *level = 10;
624                 
625                 if (queried) {
626                         i->info10.home_drive.string = s->change.home_drive;
627                         s->change.fields ^= USERMOD_FIELD_HOME_DRIVE;
628                 } else {
629                         return false;
630                 }
631         }
632         
633         if ((s->change.fields & USERMOD_FIELD_ACCT_EXPIRY) &&
634             (*level == 0 || *level == 17)) {
635                 *level = 17;
636                 i->info17.acct_expiry = timeval_to_nttime(s->change.acct_expiry);
637                 
638                 s->change.fields ^= USERMOD_FIELD_ACCT_EXPIRY;
639         }
640
641         if ((s->change.fields & USERMOD_FIELD_ACCT_FLAGS) &&
642             (*level == 0 || *level == 16)) {
643                 *level = 16;
644                 i->info16.acct_flags = s->change.acct_flags;
645                 
646                 s->change.fields ^= USERMOD_FIELD_ACCT_FLAGS;
647         }
648
649         /* We're going to be here back again soon unless all fields have been set */
650         return true;
651 }
652
653
654 static NTSTATUS usermod_change(struct composite_context *c,
655                                struct usermod_state *s)
656 {
657         struct rpc_request *query_req, *setuser_req;
658         bool do_set;
659         union samr_UserInfo *i = &s->info;
660
661         /* set the level to invalid value, so that unless setfields routine 
662            gives it a valid value we report the error correctly */
663         uint16_t level = 27;
664
665         /* prepare UserInfo level and data based on bitmask field */
666         do_set = usermod_setfields(s, &level, i, false);
667
668         if (level < 1 || level > 26) {
669                 /* apparently there's a field that the setfields routine
670                    does not know how to set */
671                 return NT_STATUS_INVALID_PARAMETER;
672         }
673
674         /* If some specific level is used to set user account data and the change
675            itself does not cover all fields then we need to query the user info
676            first, right before changing the data. Otherwise we could set required
677            fields and accidentally reset the others.
678         */
679         if (!do_set) {
680                 s->queryuser.in.user_handle = &s->user_handle;
681                 s->queryuser.in.level       = level;
682
683                 /* send query user info request to retrieve complete data of
684                    a particular info level */
685                 query_req = dcerpc_samr_QueryUserInfo_send(s->pipe, c, &s->queryuser);
686                 composite_continue_rpc(c, query_req, continue_usermod_user_queried, c);
687
688         } else {
689                 s->setuser.in.user_handle  = &s->user_handle;
690                 s->setuser.in.level        = level;
691                 s->setuser.in.info         = i;
692
693                 /* send set user info request after making required change */
694                 setuser_req = dcerpc_samr_SetUserInfo_send(s->pipe, c, &s->setuser);
695                 composite_continue_rpc(c, setuser_req, continue_usermod_user_changed, c);
696         }
697         
698         return NT_STATUS_OK;
699 }
700
701
702 /**
703  * Stage 2: Open user account
704  */
705 static void continue_usermod_user_opened(struct rpc_request *req)
706 {
707         struct composite_context *c;
708         struct usermod_state *s;
709
710         c = talloc_get_type(req->async.private_data, struct composite_context);
711         s = talloc_get_type(c->private_data, struct usermod_state);
712
713         c->status = dcerpc_ndr_request_recv(req);
714         if (!composite_is_ok(c)) return;
715
716         c->status = s->openuser.out.result;
717         if (!NT_STATUS_IS_OK(c->status)) {
718                 composite_error(c, c->status);
719                 return;
720         }
721
722         c->status = usermod_change(c, s);
723 }
724
725
726 /**
727  * Stage 2a (optional): Query the user information
728  */
729 static void continue_usermod_user_queried(struct rpc_request *req)
730 {
731         struct composite_context *c;
732         struct usermod_state *s;
733         union samr_UserInfo *i;
734         uint16_t level;
735         struct rpc_request *setuser_req;
736         
737         c = talloc_get_type(req->async.private_data, struct composite_context);
738         s = talloc_get_type(c->private_data, struct usermod_state);
739
740         i = &s->info;
741
742         /* receive samr_QueryUserInfo result */
743         c->status = dcerpc_ndr_request_recv(req);
744         if (!composite_is_ok(c)) return;
745
746         c->status = s->queryuser.out.result;
747         if (!NT_STATUS_IS_OK(c->status)) {
748                 composite_error(c, c->status);
749                 return;
750         }
751
752         /* get returned user data and make a change (potentially one
753            of many) */
754         s->info = *s->queryuser.out.info;
755
756         usermod_setfields(s, &level, i, true);
757
758         /* prepare rpc call arguments */
759         s->setuser.in.user_handle  = &s->user_handle;
760         s->setuser.in.level        = level;
761         s->setuser.in.info         = i;
762
763         /* send the rpc request */
764         setuser_req = dcerpc_samr_SetUserInfo_send(s->pipe, c, &s->setuser);
765         composite_continue_rpc(c, setuser_req, continue_usermod_user_changed, c);
766 }
767
768
769 /**
770  * Stage 3: Set new user account data
771  */
772 static void continue_usermod_user_changed(struct rpc_request *req)
773 {
774         struct composite_context *c;
775         struct usermod_state *s;
776         
777         c = talloc_get_type(req->async.private_data, struct composite_context);
778         s = talloc_get_type(c->private_data, struct usermod_state);
779
780         /* receive samr_SetUserInfo result */
781         c->status = dcerpc_ndr_request_recv(req);
782         if (!composite_is_ok(c)) return;
783
784         /* return the actual function call status */
785         c->status = s->setuser.out.result;
786         if (!NT_STATUS_IS_OK(c->status)) {
787                 composite_error(c, c->status);
788                 return;
789         }
790
791         if (s->change.fields == 0) {
792                 /* all fields have been set - we're done */
793                 composite_done(c);
794
795         } else {
796                 /* something's still not changed - repeat the procedure */
797                 c->status = usermod_change(c, s);
798         }
799 }
800
801
802 /**
803  * Sends asynchronous usermod request
804  *
805  * @param p dce/rpc call pipe
806  * @param io arguments and results of the call
807  * @param monitor monitor function for providing information about the progress
808  */
809
810 struct composite_context *libnet_rpc_usermod_send(struct dcerpc_pipe *p,
811                                                   struct libnet_rpc_usermod *io,
812                                                   void (*monitor)(struct monitor_msg*))
813 {
814         struct composite_context *c;
815         struct usermod_state *s;
816         struct rpc_request *lookup_req;
817
818         /* composite context allocation and setup */
819         c = composite_create(p, dcerpc_event_context(p));
820         if (c == NULL) return NULL;
821         s = talloc_zero(c, struct usermod_state);
822         if (composite_nomem(s, c)) return c;
823
824         c->private_data = s;
825
826         /* store parameters in the call structure */
827         s->pipe          = p;
828         s->domain_handle = io->in.domain_handle;
829         s->change        = io->in.change;
830         s->monitor_fn    = monitor;
831         
832         /* prepare rpc call arguments */
833         s->lookupname.in.domain_handle = &io->in.domain_handle;
834         s->lookupname.in.num_names     = 1;
835         s->lookupname.in.names         = talloc_zero(s, struct lsa_String);
836         s->lookupname.in.names->string = io->in.username;
837
838         /* send the rpc request */
839         lookup_req = dcerpc_samr_LookupNames_send(p, c, &s->lookupname);
840         if (composite_nomem(lookup_req, c)) return c;
841         
842         /* callback handler setup */
843         composite_continue_rpc(c, lookup_req, continue_usermod_name_found, c);
844         return c;
845 }
846
847
848 /**
849  * Waits for and receives results of asynchronous usermod call
850  *
851  * @param c composite context returned by asynchronous usermod call
852  * @param mem_ctx memory context of the call
853  * @param io pointer to results (and arguments) of the call
854  * @return nt status code of execution
855  */
856
857 NTSTATUS libnet_rpc_usermod_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
858                                  struct libnet_rpc_usermod *io)
859 {
860         NTSTATUS status;
861         
862         status = composite_wait(c);
863
864         talloc_free(c);
865         return status;
866 }
867
868
869 /**
870  * Synchronous version of usermod call
871  *
872  * @param pipe dce/rpc call pipe
873  * @param mem_ctx memory context for the call
874  * @param io arguments and results of the call
875  * @return nt status code of execution
876  */
877
878 NTSTATUS libnet_rpc_usermod(struct dcerpc_pipe *p,
879                             TALLOC_CTX *mem_ctx,
880                             struct libnet_rpc_usermod *io)
881 {
882         struct composite_context *c = libnet_rpc_usermod_send(p, io, NULL);
883         return libnet_rpc_usermod_recv(c, mem_ctx, io);
884 }