c445ef0bdc73ebd45230a71a28da67a0dea241da
[metze/samba/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,
182                 user_info.acct_name);
183         fstrcpy(state->response->data.user_info.full_name,
184                 user_info.full_name);
185         fstrcpy(state->response->data.user_info.homedir, user_info.homedir);
186         fstrcpy(state->response->data.user_info.shell, user_info.shell);
187         state->response->data.user_info.primary_gid = user_info.primary_gid;
188         if (!sid_peek_check_rid(&domain->sid, &user_info.group_sid,
189                                 &state->response->data.user_info.group_rid)) {
190                 DEBUG(1, ("Could not extract group rid out of %s\n",
191                           sid_string_dbg(&sid)));
192                 return WINBINDD_ERROR;
193         }
194
195         return WINBINDD_OK;
196 }
197
198 struct getpwsid_state {
199         struct winbindd_cli_state *state;
200         struct winbindd_domain *domain;
201         char *username;
202         char *fullname;
203         char *homedir;
204         char *shell;
205         DOM_SID user_sid;
206         uid_t uid;
207         DOM_SID group_sid;
208         gid_t gid;
209         bool username_mapped;
210 };
211
212 static void getpwsid_queryuser_recv(void *private_data, bool success,
213                                     const char *acct_name,
214                                     const char *full_name,
215                                     const char *homedir,
216                                     const char *shell,
217                                     gid_t gid,
218                                     uint32 group_rid);
219 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid);
220 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid);
221
222 static void getpwsid_queryuser(struct winbindd_cli_state *state,
223                                 const DOM_SID *sid)
224 {
225         struct getpwsid_state *s;
226
227         s = TALLOC_ZERO_P(state->mem_ctx, struct getpwsid_state);
228         if (s == NULL) {
229                 DEBUG(0, ("talloc failed\n"));
230                 goto error;
231         }
232
233         s->state = state;
234         s->domain = find_domain_from_sid_noinit(sid);
235         if (s->domain == NULL) {
236                 DEBUG(3, ("Could not find domain for sid %s\n",
237                           sid_string_dbg(sid)));
238                 goto error;
239         }
240
241         sid_copy(&s->user_sid, sid);
242
243         query_user_async(s->state->mem_ctx, s->domain, sid,
244                          getpwsid_queryuser_recv, s);
245         return;
246
247  error:
248         request_error(state);
249 }
250
251 static void getpwsid_queryuser_recv(void *private_data, bool success,
252                                     const char *acct_name,
253                                     const char *full_name,
254                                     const char *homedir,
255                                     const char *shell,
256                                     gid_t gid,
257                                     uint32 group_rid)
258 {
259         fstring username;
260         struct getpwsid_state *s =
261                 talloc_get_type_abort(private_data, struct getpwsid_state);
262         char *mapped_name;
263         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
264
265         if (!success) {
266                 DEBUG(5, ("Could not query domain %s SID %s\n",
267                           s->domain->name, sid_string_dbg(&s->user_sid)));
268                 request_error(s->state);
269                 return;
270         }
271
272         if ( acct_name && *acct_name ) {
273                 fstrcpy( username, acct_name );
274         } else {
275                 char *domain_name = NULL;
276                 enum lsa_SidType type;
277                 char *user_name = NULL;
278                 struct winbindd_domain *domain = NULL;
279
280                 domain = find_lookup_domain_from_sid(&s->user_sid);
281                 if (domain == NULL) {
282                         DEBUG(5, ("find_lookup_domain_from_sid(%s) failed\n",
283                                   sid_string_dbg(&s->user_sid)));
284                         request_error(s->state);
285                         return;
286                 }
287                 winbindd_lookup_name_by_sid(s->state->mem_ctx, domain,
288                                             &s->user_sid, &domain_name,
289                                             &user_name, &type );
290
291                 /* If this still fails we are done.  Just error out */
292                 if ( !user_name ) {
293                         DEBUG(5,("Could not obtain a name for SID %s\n",
294                                  sid_string_dbg(&s->user_sid)));
295                         request_error(s->state);
296                         return;
297                 }
298
299                 fstrcpy( username, user_name );
300         }
301
302         strlower_m( username );
303         s->username = talloc_strdup(s->state->mem_ctx, username);
304
305         nt_status = normalize_name_map(s->state->mem_ctx, s->domain,
306                                        s->username, &mapped_name);
307
308         /* Basic removal of whitespace */
309         if (NT_STATUS_IS_OK(nt_status)) {
310                 s->username = mapped_name;
311                 s->username_mapped = false;
312         }
313         /* Complete name replacement */
314         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
315                 s->username = mapped_name;
316                 s->username_mapped = true;
317         }
318         /* No change at all */
319         else {
320                 s->username_mapped = false;
321         }
322
323         s->fullname = talloc_strdup(s->state->mem_ctx, full_name);
324         s->homedir = talloc_strdup(s->state->mem_ctx, homedir);
325         s->shell = talloc_strdup(s->state->mem_ctx, shell);
326         s->gid = gid;
327         sid_copy(&s->group_sid, &s->domain->sid);
328         sid_append_rid(&s->group_sid, group_rid);
329
330         winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid,
331                                getpwsid_sid2uid_recv, s);
332 }
333
334 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid)
335 {
336         struct getpwsid_state *s =
337                 talloc_get_type_abort(private_data, struct getpwsid_state);
338
339         if (!success) {
340                 DEBUG(5, ("Could not query uid for user %s\\%s\n",
341                           s->domain->name, s->username));
342                 request_error(s->state);
343                 return;
344         }
345
346         s->uid = uid;
347         winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
348                                getpwsid_sid2gid_recv, s);
349 }
350
351 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
352 {
353         struct getpwsid_state *s =
354                 talloc_get_type_abort(private_data, struct getpwsid_state);
355         struct winbindd_pw *pw;
356         fstring output_username;
357
358         /* allow the nss backend to override the primary group ID.
359            If the gid has already been set, then keep it.
360            This makes me feel dirty.  If the nss backend already
361            gave us a gid, we don't really care whether the sid2gid()
362            call worked or not.   --jerry  */
363
364         if ( s->gid == (gid_t)-1 ) {
365
366                 if (!success) {
367                         DEBUG(5, ("Could not query gid for user %s\\%s\n",
368                                   s->domain->name, s->username));
369                         goto failed;
370                 }
371
372                 /* take what the sid2gid() call gave us */
373                 s->gid = gid;
374         }
375
376         pw = &s->state->response->data.pw;
377         pw->pw_uid = s->uid;
378         pw->pw_gid = s->gid;
379
380         /* allow username to be overridden by the alias mapping */
381
382         if ( s->username_mapped ) {
383                 fstrcpy( output_username, s->username );
384         } else {
385         fill_domain_username(output_username, s->domain->name,
386                              s->username, True);
387         }
388
389         safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
390         safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1);
391
392         if (!fillup_pw_field(lp_template_homedir(), s->username,
393                              s->domain->name, pw->pw_uid, pw->pw_gid,
394                              s->homedir, pw->pw_dir)) {
395                 DEBUG(5, ("Could not compose homedir\n"));
396                 goto failed;
397         }
398
399         if (!fillup_pw_field(lp_template_shell(), s->username,
400                              s->domain->name, pw->pw_uid, pw->pw_gid,
401                              s->shell, pw->pw_shell)) {
402                 DEBUG(5, ("Could not compose shell\n"));
403                 goto failed;
404         }
405
406         /* Password - set to "*" as we can't generate anything useful here.
407            Authentication can be done using the pam_winbind module. */
408
409         safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
410
411         request_ok(s->state);
412         return;
413
414  failed:
415         request_error(s->state);
416 }
417
418 /* Return a password structure from a username.  */
419
420 static void getpwnam_name2sid_recv(void *private_data, bool success,
421                                    const DOM_SID *sid, enum lsa_SidType type);
422
423 void winbindd_getpwnam(struct winbindd_cli_state *state)
424 {
425         struct winbindd_domain *domain;
426         fstring domname, username;
427         char *mapped_user = NULL;
428         char *domuser;
429         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
430
431         domuser = state->request->data.username;
432
433         /* Ensure null termination (it's an fstring) */
434         domuser[sizeof(state->request->data.username)-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                 domain = find_our_domain();
466                 if (domain == NULL) {
467                         DEBUG(0, ("Cannot find my primary domain "
468                                   "structure!\n"));
469                         request_error(state);
470                         return;
471                 }
472         }
473
474         if (strequal(domname, lp_workgroup()) &&
475             lp_winbind_trusted_domains_only() ) {
476                 DEBUG(7,("winbindd_getpwnam: My domain -- "
477                          "rejecting getpwnam() for %s\\%s.\n",
478                          domname, username));
479                 request_error(state);
480                 return;
481         }
482
483         /* Get rid and name type from name.  The following costs 1 packet */
484
485         winbindd_lookupname_async(state->mem_ctx, domname, username,
486                                   getpwnam_name2sid_recv, WINBINDD_GETPWNAM,
487                                   state);
488 }
489
490 static void getpwnam_name2sid_recv(void *private_data, bool success,
491                                    const DOM_SID *sid, enum lsa_SidType type)
492 {
493         struct winbindd_cli_state *state =
494                 (struct winbindd_cli_state *)private_data;
495         fstring domname, username;
496         char *domuser = state->request->data.username;
497
498         if (!success) {
499                 DEBUG(5, ("Could not lookup name for user %s\n", domuser));
500                 request_error(state);
501                 return;
502         }
503
504         if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) {
505                 DEBUG(5, ("%s is not a user\n", domuser));
506                 request_error(state);
507                 return;
508         }
509
510         if (parse_domain_user(domuser, domname, username)) {
511                 check_domain_trusted(domname, sid);
512         }
513
514         getpwsid_queryuser(state, sid);
515 }
516
517 static void getpwuid_recv(void *private_data, bool success, const char *sid)
518 {
519         struct winbindd_cli_state *state =
520                 (struct winbindd_cli_state *)private_data;
521         DOM_SID user_sid;
522
523         if (!success) {
524                 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
525                           (unsigned long)(state->request->data.uid)));
526                 request_error(state);
527                 return;
528         }
529
530         DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
531                   (unsigned long)(state->request->data.uid), sid));
532
533         if (!string_to_sid(&user_sid, sid)) {
534                 DEBUG(1,("uid2sid_recv: Could not convert sid %s "
535                         "from string\n,", sid));
536                 request_error(state);
537                 return;
538         }
539
540         getpwsid_queryuser(state, &user_sid);
541 }
542
543 /* Return a password structure given a uid number */
544 void winbindd_getpwuid(struct winbindd_cli_state *state)
545 {
546         uid_t uid = state->request->data.uid;
547
548         DEBUG(3, ("[%5lu]: getpwuid %lu\n",
549                   (unsigned long)state->pid,
550                   (unsigned long)uid));
551
552         /* always query idmap via the async interface */
553         /* if this turns to be too slow we will add here
554          * a direct query to the cache */
555         winbindd_uid2sid_async(state->mem_ctx, uid, getpwuid_recv, state);
556 }
557
558 /* Return a password structure given a sid */
559 void winbindd_getpwsid(struct winbindd_cli_state *state)
560 {
561         DOM_SID sid;
562
563         /* Ensure null termination */
564         state->request->data.sid[sizeof(state->request->data.sid)-1]='\0';
565
566         DEBUG(3, ("[%5lu]: getpwsid %s\n", (unsigned long)state->pid,
567                   state->request->data.sid));
568
569         if (!string_to_sid(&sid, state->request->data.sid)) {
570                 DEBUG(5, ("%s not a SID\n", state->request->data.sid));
571                 request_error(state);
572                 return;
573         }
574
575         getpwsid_queryuser(state, &sid);
576 }
577
578 /*
579  * set/get/endpwent functions
580  */
581
582 /* Rewind file pointer for ntdom passwd database */
583
584 static bool winbindd_setpwent_internal(struct winbindd_cli_state *state)
585 {
586         struct winbindd_domain *domain;
587
588         DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
589
590         /* Check user has enabled this */
591
592         if (!lp_winbind_enum_users()) {
593                 return False;
594         }
595
596         /* Free old static data if it exists */
597
598         if (state->getpwent_state != NULL) {
599                 free_getent_state(state->getpwent_state);
600                 state->getpwent_state = NULL;
601         }
602
603         /* Create sam pipes for each domain we know about */
604
605         for(domain = domain_list(); domain != NULL; domain = domain->next) {
606                 struct getent_state *domain_state;
607
608
609                 /* don't add our domaina if we are a PDC or if we
610                    are a member of a Samba domain */
611
612                 if ((IS_DC || lp_winbind_trusted_domains_only())
613                         && strequal(domain->name, lp_workgroup())) {
614                         continue;
615                 }
616
617                 /* Create a state record for this domain */
618
619                 domain_state = SMB_MALLOC_P(struct getent_state);
620                 if (!domain_state) {
621                         DEBUG(0, ("malloc failed\n"));
622                         return False;
623                 }
624
625                 ZERO_STRUCTP(domain_state);
626
627                 fstrcpy(domain_state->domain_name, domain->name);
628
629                 /* Add to list of open domains */
630
631                 DLIST_ADD(state->getpwent_state, domain_state);
632         }
633
634         state->getpwent_initialized = True;
635         return True;
636 }
637
638 void winbindd_setpwent(struct winbindd_cli_state *state)
639 {
640         if (winbindd_setpwent_internal(state)) {
641                 request_ok(state);
642         } else {
643                 request_error(state);
644         }
645 }
646
647 /* Close file pointer to ntdom passwd database */
648
649 void winbindd_endpwent(struct winbindd_cli_state *state)
650 {
651         DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
652
653         free_getent_state(state->getpwent_state);
654         state->getpwent_initialized = False;
655         state->getpwent_state = NULL;
656         request_ok(state);
657 }
658
659 /* Get partial list of domain users for a domain.  We fill in the sam_entries,
660    and num_sam_entries fields with domain user information.  The dispinfo_ndx
661    field is incremented to the index of the next user to fetch.  Return True if
662    some users were returned, False otherwise. */
663
664 static bool get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
665 {
666         NTSTATUS status;
667         uint32 num_entries;
668         WINBIND_USERINFO *info;
669         struct getpwent_user *name_list = NULL;
670         struct winbindd_domain *domain;
671         struct winbindd_methods *methods;
672         unsigned int i;
673
674         if (ent->num_sam_entries)
675                 return False;
676
677         if (!(domain = find_domain_from_name(ent->domain_name))) {
678                 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
679                           ent->domain_name));
680                 return False;
681         }
682
683         methods = domain->methods;
684
685         /* Free any existing user info */
686
687         SAFE_FREE(ent->sam_entries);
688         ent->num_sam_entries = 0;
689
690         /* Call query_user_list to get a list of usernames and user rids */
691
692         num_entries = 0;
693
694         status = methods->query_user_list(domain, mem_ctx, &num_entries, &info);
695
696         if (!NT_STATUS_IS_OK(status)) {
697                 DEBUG(10,("get_sam_user_entries: "
698                           "query_user_list failed with %s\n",
699                           nt_errstr(status)));
700                 return False;
701         }
702
703         if (num_entries) {
704                 name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user,
705                                             ent->num_sam_entries + num_entries);
706                 if (!name_list) {
707                         DEBUG(0,("get_sam_user_entries realloc failed.\n"));
708                         return False;
709                 }
710         }
711
712         for (i = 0; i < num_entries; i++) {
713                 /* Store account name and gecos */
714                 if (!info[i].acct_name) {
715                         fstrcpy(name_list[ent->num_sam_entries + i].name, "");
716                 } else {
717                         fstrcpy(name_list[ent->num_sam_entries + i].name,
718                                 info[i].acct_name);
719                 }
720                 if (!info[i].full_name) {
721                         fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
722                 } else {
723                         fstrcpy(name_list[ent->num_sam_entries + i].gecos,
724                                 info[i].full_name);
725                 }
726                 if (!info[i].homedir) {
727                         fstrcpy(name_list[ent->num_sam_entries + i].homedir,"");
728                 } else {
729                         fstrcpy(name_list[ent->num_sam_entries + i].homedir,
730                                 info[i].homedir);
731                 }
732                 if (!info[i].shell) {
733                         fstrcpy(name_list[ent->num_sam_entries + i].shell, "");
734                 } else {
735                         fstrcpy(name_list[ent->num_sam_entries + i].shell,
736                                 info[i].shell);
737                 }
738
739
740                 /* User and group ids */
741                 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
742                          &info[i].user_sid);
743                 sid_copy(&name_list[ent->num_sam_entries+i].group_sid,
744                          &info[i].group_sid);
745         }
746
747         ent->num_sam_entries += num_entries;
748
749         /* Fill in remaining fields */
750
751         ent->sam_entries = name_list;
752         ent->sam_entry_index = 0;
753         return ent->num_sam_entries > 0;
754 }
755
756 /* Fetch next passwd entry from ntdom database */
757
758 #define MAX_GETPWENT_USERS 500
759
760 void winbindd_getpwent(struct winbindd_cli_state *state)
761 {
762         struct getent_state *ent;
763         struct winbindd_pw *user_list;
764         int num_users, user_list_ndx;
765
766         DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
767
768         /* Check user has enabled this */
769
770         if (!lp_winbind_enum_users()) {
771                 request_error(state);
772                 return;
773         }
774
775         /* Allocate space for returning a chunk of users */
776
777         num_users = MIN(MAX_GETPWENT_USERS, state->request->data.num_entries);
778
779         if (num_users == 0) {
780                 request_error(state);
781                 return;
782         }
783
784         user_list = talloc_zero_array(state->mem_ctx, struct winbindd_pw,
785                                       num_users);
786         if (!user_list) {
787                 request_error(state);
788                 return;
789         }
790         state->response->extra_data.data = user_list;
791
792         if (!state->getpwent_initialized)
793                 winbindd_setpwent_internal(state);
794
795         if (!(ent = state->getpwent_state)) {
796                 request_error(state);
797                 return;
798         }
799
800         /* Start sending back users */
801
802         for (user_list_ndx = 0; user_list_ndx < num_users; ) {
803                 struct getpwent_user *name_list = NULL;
804                 uint32 result;
805
806                 /* Do we need to fetch another chunk of users? */
807
808                 if (ent->num_sam_entries == ent->sam_entry_index) {
809
810                         while(ent &&
811                               !get_sam_user_entries(ent, state->mem_ctx)) {
812                                 struct getent_state *next_ent;
813
814                                 /* Free state information for this domain */
815
816                                 SAFE_FREE(ent->sam_entries);
817
818                                 next_ent = ent->next;
819                                 DLIST_REMOVE(state->getpwent_state, ent);
820
821                                 SAFE_FREE(ent);
822                                 ent = next_ent;
823                         }
824
825                         /* No more domains */
826
827                         if (!ent)
828                                 break;
829                 }
830
831                 name_list = (struct getpwent_user *)ent->sam_entries;
832
833                 /* Lookup user info */
834
835                 result = winbindd_fill_pwent(
836                         state->mem_ctx,
837                         ent->domain_name,
838                         name_list[ent->sam_entry_index].name,
839                         &name_list[ent->sam_entry_index].user_sid,
840                         &name_list[ent->sam_entry_index].group_sid,
841                         name_list[ent->sam_entry_index].gecos,
842                         name_list[ent->sam_entry_index].homedir,
843                         name_list[ent->sam_entry_index].shell,
844                         &user_list[user_list_ndx]);
845
846                 /* Add user to return list */
847
848                 if (result) {
849
850                         user_list_ndx++;
851                         state->response->data.num_entries++;
852                         state->response->length += sizeof(struct winbindd_pw);
853
854                 } else
855                         DEBUG(1, ("could not lookup domain user %s\n",
856                                   name_list[ent->sam_entry_index].name));
857
858                 ent->sam_entry_index++;
859
860         }
861
862         /* Out of domains */
863
864         if (user_list_ndx > 0)
865                 request_ok(state);
866         else
867                 request_error(state);
868 }
869
870 /* List domain users without mapping to unix ids */
871 void winbindd_list_users(struct winbindd_cli_state *state)
872 {
873         winbindd_list_ent(state, LIST_USERS);
874 }