r18752: When change is a big one, it needs to be made in a couple
[jra/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 2 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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /*
22   a composite functions for user management operations (add/del/chg)
23 */
24
25 #include "includes.h"
26 #include "libcli/composite/composite.h"
27 #include "libnet/composite.h"
28 #include "libnet/userman.h"
29 #include "libnet/userinfo.h"
30 #include "librpc/gen_ndr/ndr_samr_c.h"
31
32 /*
33  * Composite USER ADD functionality
34  */
35
36 static void useradd_handler(struct rpc_request*);
37
38 enum useradd_stage { USERADD_CREATE };
39
40 struct useradd_state {
41         enum useradd_stage       stage;
42         struct dcerpc_pipe       *pipe;
43         struct rpc_request       *req;
44         struct policy_handle     domain_handle;
45         struct samr_CreateUser   createuser;
46         struct policy_handle     user_handle;
47         uint32_t                 user_rid;
48
49         /* information about the progress */
50         void (*monitor_fn)(struct monitor_msg *);
51 };
52
53
54 /**
55  * Stage 1 (and the only one for now): Create user account.
56  */
57 static NTSTATUS useradd_create(struct composite_context *c,
58                                struct useradd_state *s)
59 {
60         c->status = dcerpc_ndr_request_recv(s->req);
61         NT_STATUS_NOT_OK_RETURN(c->status);
62         
63         c->state = COMPOSITE_STATE_DONE;
64         return NT_STATUS_OK;
65 }
66
67
68 /**
69  * Event handler for asynchronous request. Handles transition through
70  * intermediate stages of the call.
71  *
72  * @param req rpc call context
73  */
74 static void useradd_handler(struct rpc_request *req)
75 {
76         struct composite_context *c = req->async.private;
77         struct useradd_state *s = talloc_get_type(c->private_data, struct useradd_state);
78         struct monitor_msg msg;
79         struct msg_rpc_create_user *rpc_create;
80         
81         switch (s->stage) {
82         case USERADD_CREATE:
83                 c->status = useradd_create(c, s);
84                 
85                 /* prepare a message to pass to monitor function */
86                 msg.type = rpc_create_user;
87                 rpc_create = talloc(s, struct msg_rpc_create_user);
88                 rpc_create->rid = *s->createuser.out.rid;
89                 msg.data = (void*)rpc_create;
90                 msg.data_size = sizeof(*rpc_create);
91                 break;
92         }
93
94         /* are we ok so far ? */
95         if (!NT_STATUS_IS_OK(c->status)) {
96                 c->state = COMPOSITE_STATE_ERROR;
97         }
98
99         /* call monitor function provided the pointer has been passed */
100         if (s->monitor_fn) {
101                 s->monitor_fn(&msg);
102         }
103
104         /* are we done yet ? */
105         if (c->state >= COMPOSITE_STATE_DONE &&
106             c->async.fn) {
107                 c->async.fn(c);
108         }
109 }
110
111
112 /**
113  * Sends asynchronous useradd request
114  *
115  * @param p dce/rpc call pipe 
116  * @param io arguments and results of the call
117  * @param monitor monitor function for providing information about the progress
118  */
119
120 struct composite_context *libnet_rpc_useradd_send(struct dcerpc_pipe *p,
121                                                   struct libnet_rpc_useradd *io,
122                                                   void (*monitor)(struct monitor_msg*))
123 {
124         struct composite_context *c;
125         struct useradd_state *s;
126
127         /* composite allocation and setup */
128         c = talloc_zero(p, struct composite_context);
129         if (c == NULL) return NULL;
130         
131         s = talloc_zero(c, struct useradd_state);
132         if (composite_nomem(s, c)) return c;
133         
134         c->state        = COMPOSITE_STATE_IN_PROGRESS;
135         c->private_data = s;
136         c->event_ctx    = dcerpc_event_context(p);
137
138         /* put passed arguments to the state structure */
139         s->domain_handle = io->in.domain_handle;
140         s->pipe          = p;
141         s->monitor_fn    = monitor;
142         
143         /* preparing parameters to send rpc request */
144         s->createuser.in.domain_handle         = &io->in.domain_handle;
145         s->createuser.in.account_name          = talloc_zero(c, struct lsa_String);
146         s->createuser.in.account_name->string  = talloc_strdup(c, io->in.username);
147         s->createuser.out.user_handle          = &s->user_handle;
148         s->createuser.out.rid                  = &s->user_rid;
149
150         /* send the request */
151         s->req = dcerpc_samr_CreateUser_send(p, c, &s->createuser);
152         if (composite_nomem(s->req, c)) return c;
153
154         /* callback handler for continuation */
155         s->req->async.callback = useradd_handler;
156         s->req->async.private  = c;
157         s->stage = USERADD_CREATE;
158
159         return c;
160 }
161
162
163 /**
164  * Waits for and receives result of asynchronous useradd call
165  * 
166  * @param c composite context returned by asynchronous useradd call
167  * @param mem_ctx memory context of the call
168  * @param io pointer to results (and arguments) of the call
169  * @return nt status code of execution
170  */
171
172 NTSTATUS libnet_rpc_useradd_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
173                                  struct libnet_rpc_useradd *io)
174 {
175         NTSTATUS status;
176         struct useradd_state *s;
177         
178         status = composite_wait(c);
179         
180         if (NT_STATUS_IS_OK(status) && io) {
181                 /* get and return result of the call */
182                 s = talloc_get_type(c->private_data, struct useradd_state);
183                 io->out.user_handle = s->user_handle;
184         }
185
186         talloc_free(c);
187         return status;
188 }
189
190
191 /**
192  * Synchronous version of useradd call
193  *
194  * @param pipe dce/rpc call pipe
195  * @param mem_ctx memory context for the call
196  * @param io arguments and results of the call
197  * @return nt status code of execution
198  */
199
200 NTSTATUS libnet_rpc_useradd(struct dcerpc_pipe *p,
201                             TALLOC_CTX *mem_ctx,
202                             struct libnet_rpc_useradd *io)
203 {
204         struct composite_context *c = libnet_rpc_useradd_send(p, io, NULL);
205         return libnet_rpc_useradd_recv(c, mem_ctx, io);
206 }
207
208
209
210 /*
211  * Composite USER DELETE functionality
212  */
213
214 static void userdel_handler(struct rpc_request*);
215
216 enum userdel_stage { USERDEL_LOOKUP, USERDEL_OPEN, USERDEL_DELETE };
217
218 struct userdel_state {
219         enum userdel_stage        stage;
220         struct dcerpc_pipe        *pipe;
221         struct rpc_request        *req;
222         struct policy_handle      domain_handle;
223         struct policy_handle      user_handle;
224         struct samr_LookupNames   lookupname;
225         struct samr_OpenUser      openuser;
226         struct samr_DeleteUser    deleteuser;
227
228         /* information about the progress */
229         void (*monitor_fn)(struct monitor_msg *);
230 };
231
232
233 /**
234  * Stage 1: Lookup the user name and resolve it to rid
235  */
236 static NTSTATUS userdel_lookup(struct composite_context *c,
237                                struct userdel_state *s)
238 {
239         /* receive samr_LookupNames result */
240         c->status = dcerpc_ndr_request_recv(s->req);
241         NT_STATUS_NOT_OK_RETURN(c->status);
242
243         /* what to do when there's no user account to delete
244            and what if there's more than one rid resolved */
245         if (!s->lookupname.out.rids.count) {
246                 c->status = NT_STATUS_NO_SUCH_USER;
247                 composite_error(c, c->status);
248
249         } else if (!s->lookupname.out.rids.count > 1) {
250                 c->status = NT_STATUS_INVALID_ACCOUNT_NAME;
251                 composite_error(c, c->status);
252         }
253
254         /* prepare the next rpc call arguments */
255         s->openuser.in.domain_handle = &s->domain_handle;
256         s->openuser.in.rid           = s->lookupname.out.rids.ids[0];
257         s->openuser.in.access_mask   = SEC_FLAG_MAXIMUM_ALLOWED;
258         s->openuser.out.user_handle  = &s->user_handle;
259
260         /* send rpc request */
261         s->req = dcerpc_samr_OpenUser_send(s->pipe, c, &s->openuser);
262         if (s->req == NULL) return NT_STATUS_NO_MEMORY;
263
264         /* callback handler setup */
265         s->req->async.callback = userdel_handler;
266         s->req->async.private  = c;
267         s->stage = USERDEL_OPEN;
268         
269         return NT_STATUS_OK;
270 }
271
272
273 /**
274  * Stage 2: Open user account.
275  */
276 static NTSTATUS userdel_open(struct composite_context *c,
277                              struct userdel_state *s)
278 {
279         /* receive samr_OpenUser result */
280         c->status = dcerpc_ndr_request_recv(s->req);
281         NT_STATUS_NOT_OK_RETURN(c->status);
282
283         /* prepare the final rpc call arguments */
284         s->deleteuser.in.user_handle   = &s->user_handle;
285         s->deleteuser.out.user_handle  = &s->user_handle;
286         
287         /* send rpc request */
288         s->req = dcerpc_samr_DeleteUser_send(s->pipe, c, &s->deleteuser);
289         if (s->req == NULL) return NT_STATUS_NO_MEMORY;
290
291         /* callback handler setup */
292         s->req->async.callback = userdel_handler;
293         s->req->async.private  = c;
294         s->stage = USERDEL_DELETE;
295         
296         return NT_STATUS_OK;
297 }
298
299
300 /**
301  * Stage 3: Delete user account
302  */
303 static NTSTATUS userdel_delete(struct composite_context *c,
304                                struct userdel_state *s)
305 {
306         /* receive samr_DeleteUser result */
307         c->status = dcerpc_ndr_request_recv(s->req);
308         NT_STATUS_NOT_OK_RETURN(c->status);
309
310         c->state = COMPOSITE_STATE_DONE;
311
312         return NT_STATUS_OK;
313 }
314
315
316 /**
317  * Event handler for asynchronous request. Handles transition through
318  * intermediate stages of the call.
319  *
320  * @param req rpc call context
321  */
322 static void userdel_handler(struct rpc_request *req)
323 {
324         struct composite_context *c;
325         struct userdel_state *s;
326         struct monitor_msg msg;
327         struct msg_rpc_lookup_name *msg_lookup;
328         struct msg_rpc_open_user *msg_open;
329
330         c = talloc_get_type(req->async.private, struct composite_context);
331         s = talloc_get_type(c->private_data, struct userdel_state);
332         
333         switch (s->stage) {
334         case USERDEL_LOOKUP:
335                 c->status = userdel_lookup(c, s);
336
337                 /* monitor message */
338                 msg.type = rpc_lookup_name;
339                 msg_lookup = talloc(s, struct msg_rpc_lookup_name);
340
341                 msg_lookup->rid   = s->lookupname.out.rids.ids;
342                 msg_lookup->count = s->lookupname.out.rids.count;
343                 msg.data = (void*)msg_lookup;
344                 msg.data_size = sizeof(*msg_lookup);
345                 break;
346
347         case USERDEL_OPEN:
348                 c->status = userdel_open(c, s);
349
350                 /* monitor message */
351                 msg.type = rpc_open_user;
352                 msg_open = talloc(s, struct msg_rpc_open_user);
353
354                 msg_open->rid         = s->openuser.in.rid;
355                 msg_open->access_mask = s->openuser.in.rid;
356                 msg.data = (void*)msg_open;
357                 msg.data_size = sizeof(*msg_open);
358                 break;
359
360         case USERDEL_DELETE:
361                 c->status = userdel_delete(c, s);
362                 
363                 /* monitor message */
364                 msg.type = rpc_delete_user;
365                 msg.data = NULL;
366                 msg.data_size = 0;
367                 break;
368         }
369
370         /* are we ok, so far ? */
371         if (!NT_STATUS_IS_OK(c->status)) {
372                 c->state = COMPOSITE_STATE_ERROR;
373         }
374
375         /* call monitor function provided the pointer has been passed */
376         if (s->monitor_fn) {
377                 s->monitor_fn(&msg);
378         }
379
380         /* are we done yet */
381         if (c->state >= COMPOSITE_STATE_DONE &&
382             c->async.fn) {
383                 c->async.fn(c);
384         }
385 }
386
387
388 /**
389  * Sends asynchronous userdel request
390  *
391  * @param p dce/rpc call pipe
392  * @param io arguments and results of the call
393  * @param monitor monitor function for providing information about the progress
394  */
395
396 struct composite_context *libnet_rpc_userdel_send(struct dcerpc_pipe *p,
397                                                   struct libnet_rpc_userdel *io,
398                                                   void (*monitor)(struct monitor_msg*))
399 {
400         struct composite_context *c;
401         struct userdel_state *s;
402
403         /* composite context allocation and setup */
404         c = talloc_zero(p, struct composite_context);
405         if (c == NULL) return NULL;
406
407         s = talloc_zero(c, struct userdel_state);
408         if (composite_nomem(s, c)) return c;
409
410         c->state         = COMPOSITE_STATE_IN_PROGRESS;
411         c->private_data  = s;
412         c->event_ctx     = dcerpc_event_context(p);
413
414         /* store function parameters in the state structure */
415         s->pipe          = p;
416         s->domain_handle = io->in.domain_handle;
417         s->monitor_fn    = monitor;
418         
419         /* preparing parameters to send rpc request */
420         s->lookupname.in.domain_handle = &io->in.domain_handle;
421         s->lookupname.in.num_names     = 1;
422         s->lookupname.in.names         = talloc_zero(s, struct lsa_String);
423         s->lookupname.in.names->string = io->in.username;
424
425         /* send the request */
426         s->req = dcerpc_samr_LookupNames_send(p, c, &s->lookupname);
427
428         /* callback handler setup */
429         s->req->async.callback = userdel_handler;
430         s->req->async.private  = c;
431         s->stage = USERDEL_LOOKUP;
432
433         return c;
434 }
435
436
437 /**
438  * Waits for and receives results of asynchronous userdel call
439  *
440  * @param c composite context returned by asynchronous userdel call
441  * @param mem_ctx memory context of the call
442  * @param io pointer to results (and arguments) of the call
443  * @return nt status code of execution
444  */
445
446 NTSTATUS libnet_rpc_userdel_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
447                                  struct libnet_rpc_userdel *io)
448 {
449         NTSTATUS status;
450         struct userdel_state *s;
451         
452         status = composite_wait(c);
453
454         if (NT_STATUS_IS_OK(status) && io) {
455                 s  = talloc_get_type(c->private_data, struct userdel_state);
456                 io->out.user_handle = s->user_handle;
457         }
458
459         talloc_free(c);
460         return status;
461 }
462
463
464 /**
465  * Synchronous version of userdel call
466  *
467  * @param pipe dce/rpc call pipe
468  * @param mem_ctx memory context for the call
469  * @param io arguments and results of the call
470  * @return nt status code of execution
471  */
472
473 NTSTATUS libnet_rpc_userdel(struct dcerpc_pipe *p,
474                             TALLOC_CTX *mem_ctx,
475                             struct libnet_rpc_userdel *io)
476 {
477         struct composite_context *c = libnet_rpc_userdel_send(p, io, NULL);
478         return libnet_rpc_userdel_recv(c, mem_ctx, io);
479 }
480
481
482 /*
483  * USER MODIFY functionality
484  */
485
486 static void usermod_handler(struct rpc_request*);
487
488 enum usermod_stage { USERMOD_LOOKUP, USERMOD_OPEN, USERMOD_QUERY, USERMOD_MODIFY };
489
490 struct usermod_state {
491         enum usermod_stage         stage;
492         struct dcerpc_pipe         *pipe;
493         struct rpc_request         *req;
494         struct policy_handle       domain_handle;
495         struct policy_handle       user_handle;
496         struct usermod_change      change;
497         union  samr_UserInfo       info;
498         struct samr_LookupNames    lookupname;
499         struct samr_OpenUser       openuser;
500         struct samr_SetUserInfo    setuser;
501         struct samr_QueryUserInfo  queryuser;
502
503         /* information about the progress */
504         void (*monitor_fn)(struct monitor_msg *);
505 };
506
507
508 /**
509  * Step 1: Lookup user name
510  */
511 static NTSTATUS usermod_lookup(struct composite_context *c,
512                                struct usermod_state *s)
513 {
514         /* receive samr_LookupNames result */
515         c->status = dcerpc_ndr_request_recv(s->req);
516         NT_STATUS_NOT_OK_RETURN(c->status);
517
518         /* what to do when there's no user account to delete
519            and what if there's more than one rid resolved */
520         if (!s->lookupname.out.rids.count) {
521                 c->status = NT_STATUS_NO_SUCH_USER;
522                 c->state  = COMPOSITE_STATE_ERROR;
523                 return c->status;
524
525         } else if (!s->lookupname.out.rids.count > 1) {
526                 c->status = NT_STATUS_INVALID_ACCOUNT_NAME;
527                 c->state  = COMPOSITE_STATE_ERROR;
528                 return c->status;
529         }
530
531         /* prepare the next rpc call */
532         s->openuser.in.domain_handle = &s->domain_handle;
533         s->openuser.in.rid           = s->lookupname.out.rids.ids[0];
534         s->openuser.in.access_mask   = SEC_FLAG_MAXIMUM_ALLOWED;
535         s->openuser.out.user_handle  = &s->user_handle;
536
537         /* send the rpc request */
538         s->req = dcerpc_samr_OpenUser_send(s->pipe, c, &s->openuser);
539
540         /* callback handler setup */
541         s->req->async.callback = usermod_handler;
542         s->req->async.private  = c;
543         s->stage = USERMOD_OPEN;
544         
545         return NT_STATUS_OK;
546 }
547
548
549 /**
550  * Choose a proper level of samr_UserInfo structure depending on required
551  * change specified by means of flags field. Subsequent calls of this
552  * function are made until there's no flags set meaning that all of the
553  * changes have been made.
554  */
555 static uint32_t usermod_setfields(struct usermod_state *s, uint16_t *level,
556                                   union samr_UserInfo *i)
557 {
558         if (s->change.fields == 0) return s->change.fields;
559
560         *level = 0;
561
562         if ((s->change.fields & USERMOD_FIELD_ACCOUNT_NAME) &&
563             (*level == 0 || *level == 7)) {
564                 *level = 7;
565                 i->info7.account_name.string = s->change.account_name;
566                 
567                 s->change.fields ^= USERMOD_FIELD_ACCOUNT_NAME;
568                 
569         }
570
571         if ((s->change.fields & USERMOD_FIELD_FULL_NAME) &&
572             (*level == 0 || *level == 8)) {
573                 *level = 8;
574                 i->info8.full_name.string = s->change.full_name;
575                 
576                 s->change.fields ^= USERMOD_FIELD_FULL_NAME;
577         }
578         
579         if ((s->change.fields & USERMOD_FIELD_DESCRIPTION) &&
580             (*level == 0 || *level == 13)) {
581                 *level = 13;
582                 i->info13.description.string = s->change.description;
583                 
584                 s->change.fields ^= USERMOD_FIELD_DESCRIPTION;
585                 
586         }
587
588         if ((s->change.fields & USERMOD_FIELD_COMMENT) &&
589             (*level == 0 || *level == 2)) {
590                 *level = 2;
591                 
592                 if (s->stage == USERMOD_QUERY) {
593                         /* the user info is obtained, so now set the required field */
594                         i->info2.comment.string = s->change.comment;
595                         s->change.fields ^= USERMOD_FIELD_COMMENT;
596                         
597                 } else {
598                         /* we need to query the user info before setting one field in it */
599                         s->stage = USERMOD_QUERY;
600                         return s->change.fields;
601                 }
602                 
603         }
604
605         if ((s->change.fields & USERMOD_FIELD_ALLOW_PASS_CHG) &&
606             (*level == 0 || *level == 3)) {
607                 *level = 3;
608                 
609                 if (s->stage == USERMOD_QUERY) {
610                         i->info3.allow_password_change = timeval_to_nttime(s->change.allow_password_change);
611                         s->change.fields ^= USERMOD_FIELD_ALLOW_PASS_CHG;
612                         
613                 } else {
614                         s->stage = USERMOD_QUERY;
615                         return s->change.fields;
616                 }
617
618         }
619         
620         if ((s->change.fields & USERMOD_FIELD_FORCE_PASS_CHG) &&
621             (*level == 0 || *level == 3)) {
622                 *level = 3;
623
624                 if (s->stage == USERMOD_QUERY) {
625                         i->info3.force_password_change = timeval_to_nttime(s->change.force_password_change);
626                         s->change.fields ^= USERMOD_FIELD_FORCE_PASS_CHG;
627                         
628                 } else {
629                         s->stage = USERMOD_QUERY;
630                         return s->change.fields;
631                 }
632                 
633         }
634
635         if ((s->change.fields & USERMOD_FIELD_LAST_LOGON) &&
636             (*level == 0 || *level == 3)) {
637                 *level = 3;
638                 
639                 if (s->stage == USERMOD_QUERY) {
640                         i->info3.last_logon = timeval_to_nttime(s->change.last_logon);
641                         s->change.fields ^= USERMOD_FIELD_LAST_LOGON;
642                 } else {
643                         s->stage = USERMOD_QUERY;
644                         return s->change.fields;
645                 }
646                 
647         }
648
649         if ((s->change.fields & USERMOD_FIELD_LAST_LOGOFF) &&
650             (*level == 0 || *level == 3)) {
651                 *level = 3;
652                 
653                 if (s->stage == USERMOD_QUERY) {
654                         i->info3.last_logoff = timeval_to_nttime(s->change.last_logoff);
655                         s->change.fields ^= USERMOD_FIELD_LAST_LOGOFF;
656                 } else {
657                         s->stage = USERMOD_QUERY;
658                         return s->change.fields;
659                 }
660
661         }
662         
663         if ((s->change.fields & USERMOD_FIELD_LAST_PASS_CHG) &&
664             (*level == 0 || *level == 3)) {
665                 *level = 3;
666                 
667                 if (s->stage == USERMOD_QUERY) {
668                         i->info3.last_password_change = timeval_to_nttime(s->change.last_password_change);
669                         s->change.fields ^= USERMOD_FIELD_LAST_PASS_CHG;
670                 } else {
671                         s->stage = USERMOD_QUERY;
672                         return s->change.fields;
673                 }
674                 
675         }
676         
677         if ((s->change.fields & USERMOD_FIELD_LOGON_SCRIPT) &&
678             (*level == 0 || *level == 11)) {
679                 *level = 11;
680                 i->info11.logon_script.string = s->change.logon_script;
681                 
682                 s->change.fields ^= USERMOD_FIELD_LOGON_SCRIPT;
683                 
684         }
685
686         if ((s->change.fields & USERMOD_FIELD_PROFILE_PATH) &&
687             (*level == 0 || *level == 12)) {
688                 *level = 12;
689                 i->info12.profile_path.string = s->change.profile_path;
690                 
691                 s->change.fields ^= USERMOD_FIELD_PROFILE_PATH;
692                 
693         }
694
695         if ((s->change.fields & USERMOD_FIELD_HOME_DIRECTORY) &&
696             (*level == 0 || *level == 3)) {
697                 *level = 3;
698                 
699                 if (s->stage == USERMOD_QUERY) {
700                         i->info3.home_directory.string = s->change.home_directory;
701                         s->change.fields ^= USERMOD_FIELD_HOME_DIRECTORY;
702                 } else {
703                         s->stage = USERMOD_QUERY;
704                         return s->change.fields;
705                 }
706                 
707         }
708
709         if ((s->change.fields & USERMOD_FIELD_HOME_DRIVE) &&
710             (*level == 0 || *level == 3)) {
711                 *level = 3;
712                 
713                 if (s->stage == USERMOD_QUERY) {
714                         i->info3.home_drive.string = s->change.home_drive;
715                         s->change.fields ^= USERMOD_FIELD_HOME_DRIVE;
716                 } else {
717                         s->stage = USERMOD_QUERY;
718                         return s->change.fields;
719                 }
720                 
721         }
722         
723         if ((s->change.fields & USERMOD_FIELD_ACCT_EXPIRY) &&
724             (*level == 0 || *level == 17)) {
725                 *level = 17;
726                 i->info17.acct_expiry = timeval_to_nttime(s->change.acct_expiry);
727                 
728                 s->change.fields ^= USERMOD_FIELD_ACCT_EXPIRY;
729                 
730         }
731
732         if ((s->change.fields & USERMOD_FIELD_ACCT_FLAGS) &&
733             (*level == 0 || *level == 16)) {
734                 *level = 16;
735                 i->info16.acct_flags = s->change.acct_flags;
736                 
737                 s->change.fields ^= USERMOD_FIELD_ACCT_FLAGS;
738         }
739
740         /* We're going to be here back again soon unless all fields have been set */
741         if (s->change.fields) {
742                 s->stage = USERMOD_OPEN;
743         } else {
744                 s->stage = USERMOD_MODIFY;
745         }
746
747         return s->change.fields;
748 }
749
750
751 static NTSTATUS usermod_change(struct composite_context *c,
752                                struct usermod_state *s)
753 {
754         union samr_UserInfo *i = &s->info;
755
756         /* set the level to invalid value, so that unless setfields routine 
757            gives it a valid value we report the error correctly */
758         uint16_t level = 27;
759
760         /* prepare UserInfo level and data based on bitmask field */
761         s->change.fields = usermod_setfields(s, &level, i);
762
763         if (level < 1 || level > 26) {
764                 /* apparently there's a field that the setfields routine
765                    does not know how to set */
766                 c->state = COMPOSITE_STATE_ERROR;
767                 return NT_STATUS_INVALID_PARAMETER;
768         }
769
770         /* If some specific level is used to set user account data and the change
771            itself does not cover all fields then we need to query the user info
772            first, right before changing the data. Otherwise we could set required
773            fields and accidentally reset the others.
774         */
775         if (s->stage == USERMOD_QUERY) {
776                 s->queryuser.in.user_handle = &s->user_handle;
777                 s->queryuser.in.level       = level;
778
779                 /* send query user info request to retrieve complete data of
780                    a particular info level */
781                 s->req = dcerpc_samr_QueryUserInfo_send(s->pipe, c, &s->queryuser);
782
783         } else {
784                 s->setuser.in.user_handle  = &s->user_handle;
785                 s->setuser.in.level        = level;
786                 s->setuser.in.info         = i;
787
788                 /* send set user info request after making required change */
789                 s->req = dcerpc_samr_SetUserInfo_send(s->pipe, c, &s->setuser);
790         }
791
792         /* callback handler setup */
793         s->req->async.callback = usermod_handler;
794         s->req->async.private  = c;
795
796         return NT_STATUS_OK;
797 }
798
799
800 /**
801  * Stage 2: Open user account
802  */
803 static NTSTATUS usermod_open(struct composite_context *c,
804                              struct usermod_state *s)
805 {
806         c->status = dcerpc_ndr_request_recv(s->req);
807         NT_STATUS_NOT_OK_RETURN(c->status);
808         
809         return usermod_change(c, s);
810 }
811
812
813 /**
814  * Stage 2a (optional): Query the user information
815  */
816 static NTSTATUS usermod_query(struct composite_context *c,
817                               struct usermod_state *s)
818 {
819         union samr_UserInfo *i = &s->info;
820         uint16_t level;
821
822         /* receive samr_QueryUserInfo result */
823         c->status = dcerpc_ndr_request_recv(s->req);
824         NT_STATUS_NOT_OK_RETURN(c->status);
825
826         /* get returned user data and make a change (potentially one
827            of many) */
828         s->info = *s->queryuser.out.info;
829
830         s->change.fields = usermod_setfields(s, &level, i);
831
832         /* prepare rpc call arguments */
833         s->setuser.in.user_handle  = &s->user_handle;
834         s->setuser.in.level        = level;
835         s->setuser.in.info         = i;
836
837         /* send the rpc request */
838         s->req = dcerpc_samr_SetUserInfo_send(s->pipe, c, &s->setuser);
839
840         /* callback handler setup */
841         s->req->async.callback = usermod_handler;
842         s->req->async.private  = c;
843
844         return NT_STATUS_OK;
845 }
846
847
848 /**
849  * Stage 3: Set new user account data
850  */
851 static NTSTATUS usermod_modify(struct composite_context *c,
852                                struct usermod_state *s)
853 {
854         /* receive samr_SetUserInfo result */
855         c->status = dcerpc_ndr_request_recv(s->req);
856         NT_STATUS_NOT_OK_RETURN(c->status);
857
858         if (s->change.fields == 0) {
859                 /* all fields have been set - we're done */
860                 c->state = COMPOSITE_STATE_DONE;
861         } else {
862                 /* something's still not changed - repeat the procedure */
863                 return usermod_change(c, s);
864         }
865
866         return NT_STATUS_OK;
867 }
868
869
870 /**
871  * Event handler for asynchronous request. Handles transition through
872  * intermediate stages of the call.
873  *
874  * @param req rpc call context
875  */
876
877 static void usermod_handler(struct rpc_request *req)
878 {
879         struct composite_context *c;
880         struct usermod_state *s;
881         struct monitor_msg msg;
882         struct msg_rpc_lookup_name *msg_lookup;
883         struct msg_rpc_open_user *msg_open;
884
885         c = talloc_get_type(req->async.private, struct composite_context);
886         s = talloc_get_type(c->private_data, struct usermod_state);
887
888         switch (s->stage) {
889         case USERMOD_LOOKUP:
890                 c->status = usermod_lookup(c, s);
891                 
892                 if (NT_STATUS_IS_OK(c->status)) {
893                         /* monitor message */
894                         msg.type = rpc_lookup_name;
895                         msg_lookup = talloc(s, struct msg_rpc_lookup_name);
896                         
897                         msg_lookup->rid   = s->lookupname.out.rids.ids;
898                         msg_lookup->count = s->lookupname.out.rids.count;
899                         msg.data = (void*)msg_lookup;
900                         msg.data_size = sizeof(*msg_lookup);
901                 }
902                 break;
903
904         case USERMOD_OPEN:
905                 c->status = usermod_open(c, s);
906
907                 if (NT_STATUS_IS_OK(c->status)) {
908                         /* monitor message */
909                         msg.type = rpc_open_user;
910                         msg_open = talloc(s, struct msg_rpc_open_user);
911                         
912                         msg_open->rid         = s->openuser.in.rid;
913                         msg_open->access_mask = s->openuser.in.rid;
914                         msg.data = (void*)msg_open;
915                         msg.data_size = sizeof(*msg_open);
916                 }
917                 break;
918
919         case USERMOD_QUERY:
920                 c->status = usermod_query(c, s);
921
922                 if (NT_STATUS_IS_OK(c->status)) {
923                         /* monitor message */
924                         msg.type = rpc_query_user;
925                         msg.data = NULL;
926                         msg.data_size = 0;
927                 }
928                 break;
929
930         case USERMOD_MODIFY:
931                 c->status = usermod_modify(c, s);
932                 
933                 if (NT_STATUS_IS_OK(c->status)) {
934                         /* monitor message */
935                         msg.type = rpc_set_user;
936                         msg.data = NULL;
937                         msg.data_size = 0;
938                 }
939                 break;
940         }
941
942         /* are we ok, so far ? */
943         if (!NT_STATUS_IS_OK(c->status)) {
944                 c->state = COMPOSITE_STATE_ERROR;
945         }
946
947         /* call monitor function provided the pointer has been passed */
948         if (s->monitor_fn) {
949                 s->monitor_fn(&msg);
950         }
951
952         /* are we done yet ? */
953         if (c->state >= COMPOSITE_STATE_DONE &&
954             c->async.fn) {
955                 c->async.fn(c);
956         }
957 }
958
959
960 /**
961  * Sends asynchronous usermod request
962  *
963  * @param p dce/rpc call pipe
964  * @param io arguments and results of the call
965  * @param monitor monitor function for providing information about the progress
966  */
967
968 struct composite_context *libnet_rpc_usermod_send(struct dcerpc_pipe *p,
969                                                   struct libnet_rpc_usermod *io,
970                                                   void (*monitor)(struct monitor_msg*))
971 {
972         struct composite_context *c;
973         struct usermod_state *s;
974
975         /* composite context allocation and setup */
976         c = talloc_zero(p, struct composite_context);
977         if (c == NULL) return NULL;
978
979         s = talloc_zero(c, struct usermod_state);
980         if (composite_nomem(s, c)) return c;
981
982         c->state        = COMPOSITE_STATE_IN_PROGRESS;
983         c->private_data = s;
984         c->event_ctx    = dcerpc_event_context(p);
985
986         /* store parameters in the call structure */
987         s->pipe          = p;
988         s->domain_handle = io->in.domain_handle;
989         s->change        = io->in.change;
990         s->monitor_fn    = monitor;
991         
992         /* prepare rpc call arguments */
993         s->lookupname.in.domain_handle = &io->in.domain_handle;
994         s->lookupname.in.num_names     = 1;
995         s->lookupname.in.names         = talloc_zero(s, struct lsa_String);
996         s->lookupname.in.names->string = io->in.username;
997
998         /* send the rpc request */
999         s->req = dcerpc_samr_LookupNames_send(p, c, &s->lookupname);
1000         
1001         /* callback handler setup */
1002         s->req->async.callback = usermod_handler;
1003         s->req->async.private  = c;
1004         s->stage = USERMOD_LOOKUP;
1005
1006         return c;
1007 }
1008
1009
1010 /**
1011  * Waits for and receives results of asynchronous usermod call
1012  *
1013  * @param c composite context returned by asynchronous usermod call
1014  * @param mem_ctx memory context of the call
1015  * @param io pointer to results (and arguments) of the call
1016  * @return nt status code of execution
1017  */
1018
1019 NTSTATUS libnet_rpc_usermod_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
1020                                  struct libnet_rpc_usermod *io)
1021 {
1022         NTSTATUS status;
1023         
1024         status = composite_wait(c);
1025
1026         talloc_free(c);
1027         return status;
1028 }
1029
1030
1031 /**
1032  * Synchronous version of usermod call
1033  *
1034  * @param pipe dce/rpc call pipe
1035  * @param mem_ctx memory context for the call
1036  * @param io arguments and results of the call
1037  * @return nt status code of execution
1038  */
1039
1040 NTSTATUS libnet_rpc_usermod(struct dcerpc_pipe *p,
1041                             TALLOC_CTX *mem_ctx,
1042                             struct libnet_rpc_usermod *io)
1043 {
1044         struct composite_context *c = libnet_rpc_usermod_send(p, io, NULL);
1045         return libnet_rpc_usermod_recv(c, mem_ctx, io);
1046 }