s3:winbindd_user: fix a debug message.
[ira/wip.git] / source3 / winbindd / winbindd_user.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon - user related functions
5
6    Copyright (C) Tim Potter 2000
7    Copyright (C) Jeremy Allison 2001.
8    Copyright (C) Gerald (Jerry) Carter 2003.
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "winbindd.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
29
30 static bool fillup_pw_field(const char *lp_template,
31                             const char *username,
32                             const char *domname,
33                             uid_t uid,
34                             gid_t gid,
35                             const char *in,
36                             fstring out)
37 {
38         char *templ;
39
40         if (out == NULL)
41                 return False;
42
43         /* The substitution of %U and %D in the 'template
44            homedir' is done by talloc_sub_specified() below.
45            If we have an in string (which means the value has already
46            been set in the nss_info backend), then use that.
47            Otherwise use the template value passed in. */
48
49         if ( in && !strequal(in,"") && lp_security() == SEC_ADS ) {
50                 templ = talloc_sub_specified(NULL, in,
51                                              username, domname,
52                                      uid, gid);
53         } else {
54                 templ = talloc_sub_specified(NULL, lp_template,
55                                              username, domname,
56                                              uid, gid);
57         }
58
59         if (!templ)
60                 return False;
61
62         safe_strcpy(out, templ, sizeof(fstring) - 1);
63         TALLOC_FREE(templ);
64
65         return True;
66
67 }
68 /* Fill a pwent structure with information we have obtained */
69
70 static bool winbindd_fill_pwent(TALLOC_CTX *ctx, char *dom_name, char *user_name,
71                                 DOM_SID *user_sid, DOM_SID *group_sid,
72                                 char *full_name, char *homedir, char *shell,
73                                 struct winbindd_pw *pw)
74 {
75         fstring output_username;
76         char *mapped_name = NULL;
77         struct winbindd_domain *domain = NULL;
78         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
79
80         if (!pw || !dom_name || !user_name)
81                 return False;
82
83         domain = find_domain_from_name_noinit(dom_name);
84         if (domain == NULL) {
85                 DEBUG(5,("winbindd_fill_pwent: Failed to find domain for %s.\n",
86                          dom_name));
87                 nt_status = NT_STATUS_NO_SUCH_DOMAIN;
88                 return false;
89         }
90
91         /* Resolve the uid number */
92
93         if (!NT_STATUS_IS_OK(idmap_sid_to_uid(domain->have_idmap_config ?
94                                               dom_name : "", user_sid,
95                                               &pw->pw_uid))) {
96                 DEBUG(1, ("error getting user id for sid %s\n",
97                           sid_string_dbg(user_sid)));
98                 return False;
99         }
100
101         /* Resolve the gid number */
102
103         if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain->have_idmap_config ?
104                                               dom_name : "", group_sid,
105                                               &pw->pw_gid))) {
106                 DEBUG(1, ("error getting group id for sid %s\n",
107                           sid_string_dbg(group_sid)));
108                 return False;
109         }
110
111         /* Username */
112
113         strlower_m(user_name);
114         nt_status = normalize_name_map(ctx, domain, user_name, &mapped_name);
115
116         /* Basic removal of whitespace */
117         if (NT_STATUS_IS_OK(nt_status)) {
118                 fill_domain_username(output_username, dom_name, mapped_name, True);
119         }
120         /* Complete name replacement */
121         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
122                 fstrcpy(output_username, mapped_name);
123         }
124         /* No change at all */
125         else {
126                 fill_domain_username(output_username, dom_name, user_name, True);
127         }
128
129         safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
130
131         /* Full name (gecos) */
132
133         safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
134
135         /* Home directory and shell */
136
137         if (!fillup_pw_field(lp_template_homedir(), user_name, dom_name,
138                              pw->pw_uid, pw->pw_gid, homedir, pw->pw_dir))
139                 return False;
140
141         if (!fillup_pw_field(lp_template_shell(), user_name, dom_name,
142                              pw->pw_uid, pw->pw_gid, shell, pw->pw_shell))
143                 return False;
144
145         /* Password - set to "*" as we can't generate anything useful here.
146            Authentication can be done using the pam_winbind module. */
147
148         safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
149
150         return True;
151 }
152
153 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
154
155 enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain,
156                                             struct winbindd_cli_state *state)
157 {
158         DOM_SID sid;
159         WINBIND_USERINFO user_info;
160         NTSTATUS status;
161
162         /* Ensure null termination */
163         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
164
165         DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid,
166                   state->request.data.sid));
167
168         if (!string_to_sid(&sid, state->request.data.sid)) {
169                 DEBUG(5, ("%s not a SID\n", state->request.data.sid));
170                 return WINBINDD_ERROR;
171         }
172
173         status = domain->methods->query_user(domain, state->mem_ctx,
174                                              &sid, &user_info);
175         if (!NT_STATUS_IS_OK(status)) {
176                 DEBUG(1, ("error getting user info for sid %s\n",
177                           sid_string_dbg(&sid)));
178                 return WINBINDD_ERROR;
179         }
180
181         fstrcpy(state->response.data.user_info.acct_name, user_info.acct_name);
182         fstrcpy(state->response.data.user_info.full_name, user_info.full_name);
183         fstrcpy(state->response.data.user_info.homedir, user_info.homedir);
184         fstrcpy(state->response.data.user_info.shell, user_info.shell);
185         state->response.data.user_info.primary_gid = user_info.primary_gid;
186         if (!sid_peek_check_rid(&domain->sid, &user_info.group_sid,
187                                 &state->response.data.user_info.group_rid)) {
188                 DEBUG(1, ("Could not extract group rid out of %s\n",
189                           sid_string_dbg(&sid)));
190                 return WINBINDD_ERROR;
191         }
192
193         return WINBINDD_OK;
194 }
195
196 struct getpwsid_state {
197         struct winbindd_cli_state *state;
198         struct winbindd_domain *domain;
199         char *username;
200         char *fullname;
201         char *homedir;
202         char *shell;
203         DOM_SID user_sid;
204         uid_t uid;
205         DOM_SID group_sid;
206         gid_t gid;
207         bool username_mapped;
208 };
209
210 static void getpwsid_queryuser_recv(void *private_data, bool success,
211                                     const char *acct_name,
212                                     const char *full_name,
213                                     const char *homedir,
214                                     const char *shell,
215                                     gid_t gid,
216                                     uint32 group_rid);
217 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid);
218 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid);
219
220 static void winbindd_getpwsid(struct winbindd_cli_state *state,
221                               const DOM_SID *sid)
222 {
223         struct getpwsid_state *s;
224
225         s = TALLOC_ZERO_P(state->mem_ctx, struct getpwsid_state);
226         if (s == NULL) {
227                 DEBUG(0, ("talloc failed\n"));
228                 goto error;
229         }
230
231         s->state = state;
232         s->domain = find_domain_from_sid_noinit(sid);
233         if (s->domain == NULL) {
234                 DEBUG(3, ("Could not find domain for sid %s\n",
235                           sid_string_dbg(sid)));
236                 goto error;
237         }
238
239         sid_copy(&s->user_sid, sid);
240
241         query_user_async(s->state->mem_ctx, s->domain, sid,
242                          getpwsid_queryuser_recv, s);
243         return;
244
245  error:
246         request_error(state);
247 }
248
249 static void getpwsid_queryuser_recv(void *private_data, bool success,
250                                     const char *acct_name,
251                                     const char *full_name,
252                                     const char *homedir,
253                                     const char *shell,
254                                     gid_t gid,
255                                     uint32 group_rid)
256 {
257         fstring username;
258         struct getpwsid_state *s =
259                 talloc_get_type_abort(private_data, struct getpwsid_state);
260         char *mapped_name;
261         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
262
263         if (!success) {
264                 DEBUG(5, ("Could not query domain %s SID %s\n",
265                           s->domain->name, sid_string_dbg(&s->user_sid)));
266                 request_error(s->state);
267                 return;
268         }
269
270         if ( acct_name && *acct_name ) {
271         fstrcpy( username, acct_name );
272         } else {
273                 char *domain_name = NULL;
274                 enum lsa_SidType type;
275                 char *user_name = NULL;
276                 struct winbindd_domain *domain = NULL;
277
278                 domain = find_lookup_domain_from_sid(&s->user_sid);
279                 if (domain == NULL) {
280                         DEBUG(5, ("find_lookup_domain_from_sid(%s) failed\n",
281                                   sid_string_dbg(&s->user_sid)));
282                         request_error(s->state);
283                         return;
284                 }
285                 winbindd_lookup_name_by_sid(s->state->mem_ctx, domain,
286                                             &s->user_sid, &domain_name,
287                                             &user_name, &type );
288
289                 /* If this still fails we ar4e done.  Just error out */
290                 if ( !user_name ) {
291                         DEBUG(5,("Could not obtain a name for SID %s\n",
292                                  sid_string_dbg(&s->user_sid)));
293                         request_error(s->state);
294                         return;
295                 }
296
297                 fstrcpy( username, user_name );
298         }
299
300         strlower_m( username );
301         s->username = talloc_strdup(s->state->mem_ctx, username);
302
303         nt_status = normalize_name_map(s->state->mem_ctx, s->domain,
304                                        s->username, &mapped_name);
305
306         /* Basic removal of whitespace */
307         if (NT_STATUS_IS_OK(nt_status)) {
308                 s->username = mapped_name;
309                 s->username_mapped = false;
310         }
311         /* Complete name replacement */
312         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
313                 s->username = mapped_name;
314                 s->username_mapped = true;
315         }
316         /* No change at all */
317         else {
318                 s->username_mapped = false;
319         }
320
321         s->fullname = talloc_strdup(s->state->mem_ctx, full_name);
322         s->homedir = talloc_strdup(s->state->mem_ctx, homedir);
323         s->shell = talloc_strdup(s->state->mem_ctx, shell);
324         s->gid = gid;
325         sid_copy(&s->group_sid, &s->domain->sid);
326         sid_append_rid(&s->group_sid, group_rid);
327
328         winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid,
329                                getpwsid_sid2uid_recv, s);
330 }
331
332 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid)
333 {
334         struct getpwsid_state *s =
335                 talloc_get_type_abort(private_data, struct getpwsid_state);
336
337         if (!success) {
338                 DEBUG(5, ("Could not query uid for user %s\\%s\n",
339                           s->domain->name, s->username));
340                 request_error(s->state);
341                 return;
342         }
343
344         s->uid = uid;
345         winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
346                                getpwsid_sid2gid_recv, s);
347 }
348
349 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
350 {
351         struct getpwsid_state *s =
352                 talloc_get_type_abort(private_data, struct getpwsid_state);
353         struct winbindd_pw *pw;
354         fstring output_username;
355
356         /* allow the nss backend to override the primary group ID.
357            If the gid has already been set, then keep it.
358            This makes me feel dirty.  If the nss backend already
359            gave us a gid, we don't really care whether the sid2gid()
360            call worked or not.   --jerry  */
361
362         if ( s->gid == (gid_t)-1 ) {
363
364                 if (!success) {
365                         DEBUG(5, ("Could not query gid for user %s\\%s\n",
366                                   s->domain->name, s->username));
367                         goto failed;
368                 }
369
370                 /* take what the sid2gid() call gave us */
371                 s->gid = gid;
372         }
373
374         pw = &s->state->response.data.pw;
375         pw->pw_uid = s->uid;
376         pw->pw_gid = s->gid;
377
378         /* allow username to be overridden by the alias mapping */
379
380         if ( s->username_mapped ) {
381                 fstrcpy( output_username, s->username );
382         } else {
383         fill_domain_username(output_username, s->domain->name,
384                              s->username, True);
385         }
386
387         safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
388         safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1);
389
390         if (!fillup_pw_field(lp_template_homedir(), s->username,
391                              s->domain->name, pw->pw_uid, pw->pw_gid,
392                              s->homedir, pw->pw_dir)) {
393                 DEBUG(5, ("Could not compose homedir\n"));
394                 goto failed;
395         }
396
397         if (!fillup_pw_field(lp_template_shell(), s->username,
398                              s->domain->name, pw->pw_uid, pw->pw_gid,
399                              s->shell, pw->pw_shell)) {
400                 DEBUG(5, ("Could not compose shell\n"));
401                 goto failed;
402         }
403
404         /* Password - set to "*" as we can't generate anything useful here.
405            Authentication can be done using the pam_winbind module. */
406
407         safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
408
409         request_ok(s->state);
410         return;
411
412  failed:
413         request_error(s->state);
414 }
415
416 /* Return a password structure from a username.  */
417
418 static void getpwnam_name2sid_recv(void *private_data, bool success,
419                                    const DOM_SID *sid, enum lsa_SidType type);
420
421 void winbindd_getpwnam(struct winbindd_cli_state *state)
422 {
423         struct winbindd_domain *domain;
424         fstring domname, username;
425         char *mapped_user = NULL;
426         char *domuser;
427         size_t dusize;
428         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
429
430         domuser = state->request.data.username;
431         dusize = sizeof(state->request.data.username);
432
433         /* Ensure null termination (it's an fstring) */
434         domuser[dusize-1] = '\0';
435
436         DEBUG(3, ("[%5lu]: getpwnam %s\n",
437                   (unsigned long)state->pid,
438                   domuser));
439
440         nt_status = normalize_name_unmap(state->mem_ctx, domuser,
441                                          &mapped_user);
442
443         /* If we could not convert from an aliased name or a
444            normalized name, then just use the original name */
445
446         if (!NT_STATUS_IS_OK(nt_status) &&
447             !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
448         {
449                 mapped_user = domuser;
450         }
451
452         if (!parse_domain_user(mapped_user, domname, username)) {
453                 DEBUG(5, ("Could not parse domain user: %s\n", domuser));
454                 request_error(state);
455                 return;
456         }
457
458         /* Get info for the domain */
459
460         domain = find_domain_from_name_noinit(domname);
461
462         if (domain == NULL) {
463                 DEBUG(7, ("could not find domain entry for domain %s.  "
464                           "Using primary domain\n", domname));
465                 if ( (domain = find_our_domain()) == NULL ) {
466                         DEBUG(0,("Cannot find my primary domain structure!\n"));
467                         request_error(state);
468                         return;
469                 }
470         }
471
472         if (strequal(domname, lp_workgroup()) &&
473             lp_winbind_trusted_domains_only() ) {
474                 DEBUG(7,("winbindd_getpwnam: My domain -- "
475                          "rejecting getpwnam() for %s\\%s.\n",
476                          domname, username));
477                 request_error(state);
478                 return;
479         }
480
481         /* Get rid and name type from name.  The following costs 1 packet */
482
483         winbindd_lookupname_async(state->mem_ctx, domname, username,
484                                   getpwnam_name2sid_recv, WINBINDD_GETPWNAM,
485                                   state);
486 }
487
488 static void getpwnam_name2sid_recv(void *private_data, bool success,
489                                    const DOM_SID *sid, enum lsa_SidType type)
490 {
491         struct winbindd_cli_state *state =
492                 (struct winbindd_cli_state *)private_data;
493         fstring domname, username;
494         char *domuser = state->request.data.username;
495
496         if (!success) {
497                 DEBUG(5, ("Could not lookup name for user %s\n", domuser));
498                 request_error(state);
499                 return;
500         }
501
502         if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) {
503                 DEBUG(5, ("%s is not a user\n", domuser));
504                 request_error(state);
505                 return;
506         }
507
508         if (parse_domain_user(domuser, domname, username)) {
509                 check_domain_trusted(domname, sid);
510         }
511
512         winbindd_getpwsid(state, sid);
513 }
514
515 static void getpwuid_recv(void *private_data, bool success, const char *sid)
516 {
517         struct winbindd_cli_state *state =
518                 (struct winbindd_cli_state *)private_data;
519         DOM_SID user_sid;
520
521         if (!success) {
522                 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
523                           (unsigned long)(state->request.data.uid)));
524                 request_error(state);
525                 return;
526         }
527
528         DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
529                   (unsigned long)(state->request.data.uid), sid));
530
531         if (!string_to_sid(&user_sid, sid)) {
532                 DEBUG(1,("uid2sid_recv: Could not convert sid %s "
533                         "from string\n,", sid));
534                 request_error(state);
535                 return;
536         }
537
538         winbindd_getpwsid(state, &user_sid);
539 }
540
541 /* Return a password structure given a uid number */
542 void winbindd_getpwuid(struct winbindd_cli_state *state)
543 {
544         uid_t uid = state->request.data.uid;
545
546         DEBUG(3, ("[%5lu]: getpwuid %lu\n",
547                   (unsigned long)state->pid,
548                   (unsigned long)uid));
549
550         /* always query idmap via the async interface */
551         /* if this turns to be too slow we will add here
552          * a direct query to the cache */
553         winbindd_uid2sid_async(state->mem_ctx, uid, getpwuid_recv, state);
554 }
555
556 /*
557  * set/get/endpwent functions
558  */
559
560 /* Rewind file pointer for ntdom passwd database */
561
562 static bool winbindd_setpwent_internal(struct winbindd_cli_state *state)
563 {
564         struct winbindd_domain *domain;
565
566         DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
567
568         /* Check user has enabled this */
569
570         if (!lp_winbind_enum_users()) {
571                 return False;
572         }
573
574         /* Free old static data if it exists */
575
576         if (state->getpwent_state != NULL) {
577                 free_getent_state(state->getpwent_state);
578                 state->getpwent_state = NULL;
579         }
580
581         /* Create sam pipes for each domain we know about */
582
583         for(domain = domain_list(); domain != NULL; domain = domain->next) {
584                 struct getent_state *domain_state;
585
586
587                 /* don't add our domaina if we are a PDC or if we
588                    are a member of a Samba domain */
589
590                 if ((IS_DC || lp_winbind_trusted_domains_only())
591                         && strequal(domain->name, lp_workgroup())) {
592                         continue;
593                 }
594
595                 /* Create a state record for this domain */
596
597                 domain_state = SMB_MALLOC_P(struct getent_state);
598                 if (!domain_state) {
599                         DEBUG(0, ("malloc failed\n"));
600                         return False;
601                 }
602
603                 ZERO_STRUCTP(domain_state);
604
605                 fstrcpy(domain_state->domain_name, domain->name);
606
607                 /* Add to list of open domains */
608
609                 DLIST_ADD(state->getpwent_state, domain_state);
610         }
611
612         state->getpwent_initialized = True;
613         return True;
614 }
615
616 void winbindd_setpwent(struct winbindd_cli_state *state)
617 {
618         if (winbindd_setpwent_internal(state)) {
619                 request_ok(state);
620         } else {
621                 request_error(state);
622         }
623 }
624
625 /* Close file pointer to ntdom passwd database */
626
627 void winbindd_endpwent(struct winbindd_cli_state *state)
628 {
629         DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
630
631         free_getent_state(state->getpwent_state);
632         state->getpwent_initialized = False;
633         state->getpwent_state = NULL;
634         request_ok(state);
635 }
636
637 /* Get partial list of domain users for a domain.  We fill in the sam_entries,
638    and num_sam_entries fields with domain user information.  The dispinfo_ndx
639    field is incremented to the index of the next user to fetch.  Return True if
640    some users were returned, False otherwise. */
641
642 static bool get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
643 {
644         NTSTATUS status;
645         uint32 num_entries;
646         WINBIND_USERINFO *info;
647         struct getpwent_user *name_list = NULL;
648         struct winbindd_domain *domain;
649         struct winbindd_methods *methods;
650         unsigned int i;
651
652         if (ent->num_sam_entries)
653                 return False;
654
655         if (!(domain = find_domain_from_name(ent->domain_name))) {
656                 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
657                           ent->domain_name));
658                 return False;
659         }
660
661         methods = domain->methods;
662
663         /* Free any existing user info */
664
665         SAFE_FREE(ent->sam_entries);
666         ent->num_sam_entries = 0;
667
668         /* Call query_user_list to get a list of usernames and user rids */
669
670         num_entries = 0;
671
672         status = methods->query_user_list(domain, mem_ctx, &num_entries, &info);
673
674         if (!NT_STATUS_IS_OK(status)) {
675                 DEBUG(10,("get_sam_user_entries: "
676                           "query_user_list failed with %s\n",
677                           nt_errstr(status)));
678                 return False;
679         }
680
681         if (num_entries) {
682                 name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user,
683                                             ent->num_sam_entries + num_entries);
684                 if (!name_list) {
685                         DEBUG(0,("get_sam_user_entries realloc failed.\n"));
686                         return False;
687                 }
688         }
689
690         for (i = 0; i < num_entries; i++) {
691                 /* Store account name and gecos */
692                 if (!info[i].acct_name) {
693                         fstrcpy(name_list[ent->num_sam_entries + i].name, "");
694                 } else {
695                         fstrcpy(name_list[ent->num_sam_entries + i].name,
696                                 info[i].acct_name);
697                 }
698                 if (!info[i].full_name) {
699                         fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
700                 } else {
701                         fstrcpy(name_list[ent->num_sam_entries + i].gecos,
702                                 info[i].full_name);
703                 }
704                 if (!info[i].homedir) {
705                         fstrcpy(name_list[ent->num_sam_entries + i].homedir,"");
706                 } else {
707                         fstrcpy(name_list[ent->num_sam_entries + i].homedir,
708                                 info[i].homedir);
709                 }
710                 if (!info[i].shell) {
711                         fstrcpy(name_list[ent->num_sam_entries + i].shell, "");
712                 } else {
713                         fstrcpy(name_list[ent->num_sam_entries + i].shell,
714                                 info[i].shell);
715                 }
716
717
718                 /* User and group ids */
719                 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
720                          &info[i].user_sid);
721                 sid_copy(&name_list[ent->num_sam_entries+i].group_sid,
722                          &info[i].group_sid);
723         }
724
725         ent->num_sam_entries += num_entries;
726
727         /* Fill in remaining fields */
728
729         ent->sam_entries = name_list;
730         ent->sam_entry_index = 0;
731         return ent->num_sam_entries > 0;
732 }
733
734 /* Fetch next passwd entry from ntdom database */
735
736 #define MAX_GETPWENT_USERS 500
737
738 void winbindd_getpwent(struct winbindd_cli_state *state)
739 {
740         struct getent_state *ent;
741         struct winbindd_pw *user_list;
742         int num_users, user_list_ndx;
743
744         DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
745
746         /* Check user has enabled this */
747
748         if (!lp_winbind_enum_users()) {
749                 request_error(state);
750                 return;
751         }
752
753         /* Allocate space for returning a chunk of users */
754
755         num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
756
757         if (num_users == 0) {
758                 request_error(state);
759                 return;
760         }
761
762         user_list = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users);
763         if (!user_list) {
764                 request_error(state);
765                 return;
766         }
767         /* will be freed by process_request() */
768         state->response.extra_data.data = user_list;
769
770         memset(user_list, 0, num_users * sizeof(struct winbindd_pw));
771
772         if (!state->getpwent_initialized)
773                 winbindd_setpwent_internal(state);
774
775         if (!(ent = state->getpwent_state)) {
776                 request_error(state);
777                 return;
778         }
779
780         /* Start sending back users */
781
782         for (user_list_ndx = 0; user_list_ndx < num_users; ) {
783                 struct getpwent_user *name_list = NULL;
784                 uint32 result;
785
786                 /* Do we need to fetch another chunk of users? */
787
788                 if (ent->num_sam_entries == ent->sam_entry_index) {
789
790                         while(ent &&
791                               !get_sam_user_entries(ent, state->mem_ctx)) {
792                                 struct getent_state *next_ent;
793
794                                 /* Free state information for this domain */
795
796                                 SAFE_FREE(ent->sam_entries);
797
798                                 next_ent = ent->next;
799                                 DLIST_REMOVE(state->getpwent_state, ent);
800
801                                 SAFE_FREE(ent);
802                                 ent = next_ent;
803                         }
804
805                         /* No more domains */
806
807                         if (!ent)
808                                 break;
809                 }
810
811                 name_list = (struct getpwent_user *)ent->sam_entries;
812
813                 /* Lookup user info */
814
815                 result = winbindd_fill_pwent(
816                         state->mem_ctx,
817                         ent->domain_name,
818                         name_list[ent->sam_entry_index].name,
819                         &name_list[ent->sam_entry_index].user_sid,
820                         &name_list[ent->sam_entry_index].group_sid,
821                         name_list[ent->sam_entry_index].gecos,
822                         name_list[ent->sam_entry_index].homedir,
823                         name_list[ent->sam_entry_index].shell,
824                         &user_list[user_list_ndx]);
825
826                 /* Add user to return list */
827
828                 if (result) {
829
830                         user_list_ndx++;
831                         state->response.data.num_entries++;
832                         state->response.length += sizeof(struct winbindd_pw);
833
834                 } else
835                         DEBUG(1, ("could not lookup domain user %s\n",
836                                   name_list[ent->sam_entry_index].name));
837
838                 ent->sam_entry_index++;
839
840         }
841
842         /* Out of domains */
843
844         if (user_list_ndx > 0)
845                 request_ok(state);
846         else
847                 request_error(state);
848 }
849
850 /* List domain users without mapping to unix ids */
851 void winbindd_list_users(struct winbindd_cli_state *state)
852 {
853         winbindd_list_ent(state, LIST_USERS);
854 }