idmap_gid_to_sid: Fix a cut-a-npaste error.
[kai/samba.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(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
77         if (!pw || !dom_name || !user_name)
78                 return False;
79
80         /* Resolve the uid number */
81
82         if (!NT_STATUS_IS_OK(idmap_sid_to_uid(dom_name, user_sid,
83                                               &pw->pw_uid))) {
84                 DEBUG(1, ("error getting user id for sid %s\n",
85                           sid_string_dbg(user_sid)));
86                 return False;
87         }
88
89         /* Resolve the gid number */
90
91         if (!NT_STATUS_IS_OK(idmap_sid_to_gid(dom_name, group_sid,
92                                               &pw->pw_gid))) {
93                 DEBUG(1, ("error getting group id for sid %s\n",
94                           sid_string_dbg(group_sid)));
95                 return False;
96         }
97
98         strlower_m(user_name);
99
100         /* Username */
101
102         fill_domain_username(output_username, dom_name, user_name, True);
103
104         safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
105
106         /* Full name (gecos) */
107
108         safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
109
110         /* Home directory and shell */
111
112         if (!fillup_pw_field(lp_template_homedir(), user_name, dom_name,
113                              pw->pw_uid, pw->pw_gid, homedir, pw->pw_dir))
114                 return False;
115
116         if (!fillup_pw_field(lp_template_shell(), user_name, dom_name,
117                              pw->pw_uid, pw->pw_gid, shell, pw->pw_shell))
118                 return False;
119
120         /* Password - set to "*" as we can't generate anything useful here.
121            Authentication can be done using the pam_winbind module. */
122
123         safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
124
125         return True;
126 }
127
128 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
129
130 enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain,
131                                             struct winbindd_cli_state *state)
132 {
133         DOM_SID sid;
134         WINBIND_USERINFO user_info;
135         NTSTATUS status;
136
137         /* Ensure null termination */
138         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
139
140         DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid,
141                   state->request.data.sid));
142
143         if (!string_to_sid(&sid, state->request.data.sid)) {
144                 DEBUG(5, ("%s not a SID\n", state->request.data.sid));
145                 return WINBINDD_ERROR;
146         }
147
148         status = domain->methods->query_user(domain, state->mem_ctx,
149                                              &sid, &user_info);
150         if (!NT_STATUS_IS_OK(status)) {
151                 DEBUG(1, ("error getting user info for sid %s\n",
152                           sid_string_dbg(&sid)));
153                 return WINBINDD_ERROR;
154         }
155
156         fstrcpy(state->response.data.user_info.acct_name, user_info.acct_name);
157         fstrcpy(state->response.data.user_info.full_name, user_info.full_name);
158         fstrcpy(state->response.data.user_info.homedir, user_info.homedir);
159         fstrcpy(state->response.data.user_info.shell, user_info.shell);
160         state->response.data.user_info.primary_gid = user_info.primary_gid;
161         if (!sid_peek_check_rid(&domain->sid, &user_info.group_sid,
162                                 &state->response.data.user_info.group_rid)) {
163                 DEBUG(1, ("Could not extract group rid out of %s\n",
164                           sid_string_dbg(&sid)));
165                 return WINBINDD_ERROR;
166         }
167
168         return WINBINDD_OK;
169 }
170
171 struct getpwsid_state {
172         struct winbindd_cli_state *state;
173         struct winbindd_domain *domain;
174         char *username;
175         char *fullname;
176         char *homedir;
177         char *shell;
178         DOM_SID user_sid;
179         uid_t uid;
180         DOM_SID group_sid;
181         gid_t gid;
182 };
183
184 static void getpwsid_queryuser_recv(void *private_data, bool success,
185                                     const char *acct_name,
186                                     const char *full_name,
187                                     const char *homedir,
188                                     const char *shell,
189                                     gid_t gid,
190                                     uint32 group_rid);
191 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid);
192 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid);
193
194 static void winbindd_getpwsid(struct winbindd_cli_state *state,
195                               const DOM_SID *sid)
196 {
197         struct getpwsid_state *s;
198
199         s = TALLOC_ZERO_P(state->mem_ctx, struct getpwsid_state);
200         if (s == NULL) {
201                 DEBUG(0, ("talloc failed\n"));
202                 goto error;
203         }
204
205         s->state = state;
206         s->domain = find_domain_from_sid_noinit(sid);
207         if (s->domain == NULL) {
208                 DEBUG(3, ("Could not find domain for sid %s\n",
209                           sid_string_dbg(sid)));
210                 goto error;
211         }
212
213         sid_copy(&s->user_sid, sid);
214
215         query_user_async(s->state->mem_ctx, s->domain, sid,
216                          getpwsid_queryuser_recv, s);
217         return;
218
219  error:
220         request_error(state);
221 }
222
223 static void getpwsid_queryuser_recv(void *private_data, bool success,
224                                     const char *acct_name,
225                                     const char *full_name,
226                                     const char *homedir,
227                                     const char *shell,
228                                     gid_t gid,
229                                     uint32 group_rid)
230 {
231         fstring username;
232         struct getpwsid_state *s =
233                 talloc_get_type_abort(private_data, struct getpwsid_state);
234
235         if (!success) {
236                 DEBUG(5, ("Could not query domain %s SID %s\n",
237                           s->domain->name, sid_string_dbg(&s->user_sid)));
238                 request_error(s->state);
239                 return;
240         }
241
242         if ( acct_name && *acct_name ) {
243         fstrcpy( username, acct_name );
244         } else {
245                 char *domain_name = NULL;
246                 enum lsa_SidType type;
247                 char *user_name = NULL;
248                 struct winbindd_domain *domain = NULL;
249
250                 domain = find_lookup_domain_from_sid(&s->user_sid);
251                 if (domain == NULL) {
252                         DEBUG(5, ("find_lookup_domain_from_sid(%s) failed\n",
253                                   sid_string_dbg(&s->user_sid)));
254                         request_error(s->state);
255                         return;
256                 }
257                 winbindd_lookup_name_by_sid(s->state->mem_ctx, domain,
258                                             &s->user_sid, &domain_name,
259                                             &user_name, &type );
260
261                 /* If this still fails we ar4e done.  Just error out */
262                 if ( !user_name ) {
263                         DEBUG(5,("Could not obtain a name for SID %s\n",
264                                  sid_string_dbg(&s->user_sid)));
265                         request_error(s->state);
266                         return;
267                 }
268
269                 fstrcpy( username, user_name );
270         }
271
272         strlower_m( username );
273         s->username = talloc_strdup(s->state->mem_ctx, username);
274
275         ws_name_replace( s->username, WB_REPLACE_CHAR );
276
277         s->fullname = talloc_strdup(s->state->mem_ctx, full_name);
278         s->homedir = talloc_strdup(s->state->mem_ctx, homedir);
279         s->shell = talloc_strdup(s->state->mem_ctx, shell);
280         s->gid = gid;
281         sid_copy(&s->group_sid, &s->domain->sid);
282         sid_append_rid(&s->group_sid, group_rid);
283
284         winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid,
285                                getpwsid_sid2uid_recv, s);
286 }
287
288 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid)
289 {
290         struct getpwsid_state *s =
291                 talloc_get_type_abort(private_data, struct getpwsid_state);
292
293         if (!success) {
294                 DEBUG(5, ("Could not query uid for user %s\\%s\n",
295                           s->domain->name, s->username));
296                 request_error(s->state);
297                 return;
298         }
299
300         s->uid = uid;
301         winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
302                                getpwsid_sid2gid_recv, s);
303 }
304
305 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
306 {
307         struct getpwsid_state *s =
308                 talloc_get_type_abort(private_data, struct getpwsid_state);
309         struct winbindd_pw *pw;
310         fstring output_username;
311
312         /* allow the nss backend to override the primary group ID.
313            If the gid has already been set, then keep it.
314            This makes me feel dirty.  If the nss backend already
315            gave us a gid, we don't really care whether the sid2gid()
316            call worked or not.   --jerry  */
317
318         if ( s->gid == (gid_t)-1 ) {
319
320                 if (!success) {
321                         DEBUG(5, ("Could not query gid for user %s\\%s\n",
322                                   s->domain->name, s->username));
323                         goto failed;
324                 }
325
326                 /* take what the sid2gid() call gave us */
327                 s->gid = gid;
328         }
329
330         pw = &s->state->response.data.pw;
331         pw->pw_uid = s->uid;
332         pw->pw_gid = s->gid;
333         fill_domain_username(output_username, s->domain->name,
334                              s->username, True);
335         safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
336         safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1);
337
338         if (!fillup_pw_field(lp_template_homedir(), s->username,
339                              s->domain->name, pw->pw_uid, pw->pw_gid,
340                              s->homedir, pw->pw_dir)) {
341                 DEBUG(5, ("Could not compose homedir\n"));
342                 goto failed;
343         }
344
345         if (!fillup_pw_field(lp_template_shell(), s->username,
346                              s->domain->name, pw->pw_uid, pw->pw_gid,
347                              s->shell, pw->pw_shell)) {
348                 DEBUG(5, ("Could not compose shell\n"));
349                 goto failed;
350         }
351
352         /* Password - set to "*" as we can't generate anything useful here.
353            Authentication can be done using the pam_winbind module. */
354
355         safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
356
357         request_ok(s->state);
358         return;
359
360  failed:
361         request_error(s->state);
362 }
363
364 /* Return a password structure from a username.  */
365
366 static void getpwnam_name2sid_recv(void *private_data, bool success,
367                                    const DOM_SID *sid, enum lsa_SidType type);
368
369 void winbindd_getpwnam(struct winbindd_cli_state *state)
370 {
371         struct winbindd_domain *domain;
372         fstring domname, username;
373         char *domuser;
374         size_t dusize;
375
376         domuser = state->request.data.username;
377         dusize = sizeof(state->request.data.username);
378
379         /* Ensure null termination (it's an fstring) */
380         domuser[dusize-1] = '\0';
381
382         DEBUG(3, ("[%5lu]: getpwnam %s\n",
383                   (unsigned long)state->pid,
384                   domuser));
385
386         ws_name_return(domuser, WB_REPLACE_CHAR);
387
388         if (!parse_domain_user(domuser, domname, username)) {
389                 DEBUG(5, ("Could not parse domain user: %s\n", domuser));
390                 request_error(state);
391                 return;
392         }
393
394         /* Get info for the domain */
395
396         domain = find_domain_from_name(domname);
397
398         if (domain == NULL) {
399                 DEBUG(7, ("could not find domain entry for domain %s.  "
400                           "Using primary domain\n", domname));
401                 if ( (domain = find_our_domain()) == NULL ) {
402                         DEBUG(0,("Cannot find my primary domain structure!\n"));
403                         request_error(state);
404                         return;
405                 }
406         }
407
408         if (strequal(domname, lp_workgroup()) &&
409             lp_winbind_trusted_domains_only() ) {
410                 DEBUG(7,("winbindd_getpwnam: My domain -- "
411                          "rejecting getpwnam() for %s\\%s.\n",
412                          domname, username));
413                 request_error(state);
414                 return;
415         }
416
417         /* Get rid and name type from name.  The following costs 1 packet */
418
419         winbindd_lookupname_async(state->mem_ctx, domname, username,
420                                   getpwnam_name2sid_recv, WINBINDD_GETPWNAM,
421                                   state);
422 }
423
424 static void getpwnam_name2sid_recv(void *private_data, bool success,
425                                    const DOM_SID *sid, enum lsa_SidType type)
426 {
427         struct winbindd_cli_state *state =
428                 (struct winbindd_cli_state *)private_data;
429         fstring domname, username;
430         char *domuser = state->request.data.username;
431
432         if (!success) {
433                 DEBUG(5, ("Could not lookup name for user %s\n", domuser));
434                 request_error(state);
435                 return;
436         }
437
438         if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) {
439                 DEBUG(5, ("%s is not a user\n", domuser));
440                 request_error(state);
441                 return;
442         }
443
444         if (parse_domain_user(domuser, domname, username)) {
445                 check_domain_trusted(domname, sid);
446         }
447
448         winbindd_getpwsid(state, sid);
449 }
450
451 static void getpwuid_recv(void *private_data, bool success, const char *sid)
452 {
453         struct winbindd_cli_state *state =
454                 (struct winbindd_cli_state *)private_data;
455         DOM_SID user_sid;
456
457         if (!success) {
458                 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
459                           (unsigned long)(state->request.data.uid)));
460                 request_error(state);
461                 return;
462         }
463
464         DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
465                   (unsigned long)(state->request.data.uid), sid));
466
467         string_to_sid(&user_sid, sid);
468         winbindd_getpwsid(state, &user_sid);
469 }
470
471 /* Return a password structure given a uid number */
472 void winbindd_getpwuid(struct winbindd_cli_state *state)
473 {
474         uid_t uid = state->request.data.uid;
475
476         DEBUG(3, ("[%5lu]: getpwuid %lu\n",
477                   (unsigned long)state->pid,
478                   (unsigned long)uid));
479
480         /* always query idmap via the async interface */
481         /* if this turns to be too slow we will add here
482          * a direct query to the cache */
483         winbindd_uid2sid_async(state->mem_ctx, uid, getpwuid_recv, state);
484 }
485
486 /*
487  * set/get/endpwent functions
488  */
489
490 /* Rewind file pointer for ntdom passwd database */
491
492 static bool winbindd_setpwent_internal(struct winbindd_cli_state *state)
493 {
494         struct winbindd_domain *domain;
495
496         DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
497
498         /* Check user has enabled this */
499
500         if (!lp_winbind_enum_users()) {
501                 return False;
502         }
503
504         /* Free old static data if it exists */
505
506         if (state->getpwent_state != NULL) {
507                 free_getent_state(state->getpwent_state);
508                 state->getpwent_state = NULL;
509         }
510
511         /* Create sam pipes for each domain we know about */
512
513         for(domain = domain_list(); domain != NULL; domain = domain->next) {
514                 struct getent_state *domain_state;
515
516
517                 /* don't add our domaina if we are a PDC or if we
518                    are a member of a Samba domain */
519
520                 if ((IS_DC || lp_winbind_trusted_domains_only())
521                         && strequal(domain->name, lp_workgroup())) {
522                         continue;
523                 }
524
525                 /* Create a state record for this domain */
526
527                 domain_state = SMB_MALLOC_P(struct getent_state);
528                 if (!domain_state) {
529                         DEBUG(0, ("malloc failed\n"));
530                         return False;
531                 }
532
533                 ZERO_STRUCTP(domain_state);
534
535                 fstrcpy(domain_state->domain_name, domain->name);
536
537                 /* Add to list of open domains */
538
539                 DLIST_ADD(state->getpwent_state, domain_state);
540         }
541
542         state->getpwent_initialized = True;
543         return True;
544 }
545
546 void winbindd_setpwent(struct winbindd_cli_state *state)
547 {
548         if (winbindd_setpwent_internal(state)) {
549                 request_ok(state);
550         } else {
551                 request_error(state);
552         }
553 }
554
555 /* Close file pointer to ntdom passwd database */
556
557 void winbindd_endpwent(struct winbindd_cli_state *state)
558 {
559         DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
560
561         free_getent_state(state->getpwent_state);
562         state->getpwent_initialized = False;
563         state->getpwent_state = NULL;
564         request_ok(state);
565 }
566
567 /* Get partial list of domain users for a domain.  We fill in the sam_entries,
568    and num_sam_entries fields with domain user information.  The dispinfo_ndx
569    field is incremented to the index of the next user to fetch.  Return True if
570    some users were returned, False otherwise. */
571
572 static bool get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
573 {
574         NTSTATUS status;
575         uint32 num_entries;
576         WINBIND_USERINFO *info;
577         struct getpwent_user *name_list = NULL;
578         struct winbindd_domain *domain;
579         struct winbindd_methods *methods;
580         unsigned int i;
581
582         if (ent->num_sam_entries)
583                 return False;
584
585         if (!(domain = find_domain_from_name(ent->domain_name))) {
586                 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
587                           ent->domain_name));
588                 return False;
589         }
590
591         methods = domain->methods;
592
593         /* Free any existing user info */
594
595         SAFE_FREE(ent->sam_entries);
596         ent->num_sam_entries = 0;
597
598         /* Call query_user_list to get a list of usernames and user rids */
599
600         num_entries = 0;
601
602         status = methods->query_user_list(domain, mem_ctx, &num_entries, &info);
603
604         if (!NT_STATUS_IS_OK(status)) {
605                 DEBUG(10,("get_sam_user_entries: "
606                           "query_user_list failed with %s\n",
607                           nt_errstr(status)));
608                 return False;
609         }
610
611         if (num_entries) {
612                 name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user,
613                                             ent->num_sam_entries + num_entries);
614                 if (!name_list) {
615                         DEBUG(0,("get_sam_user_entries realloc failed.\n"));
616                         return False;
617                 }
618         }
619
620         for (i = 0; i < num_entries; i++) {
621                 /* Store account name and gecos */
622                 if (!info[i].acct_name) {
623                         fstrcpy(name_list[ent->num_sam_entries + i].name, "");
624                 } else {
625                         fstrcpy(name_list[ent->num_sam_entries + i].name,
626                                 info[i].acct_name);
627                 }
628                 if (!info[i].full_name) {
629                         fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
630                 } else {
631                         fstrcpy(name_list[ent->num_sam_entries + i].gecos,
632                                 info[i].full_name);
633                 }
634                 if (!info[i].homedir) {
635                         fstrcpy(name_list[ent->num_sam_entries + i].homedir,"");
636                 } else {
637                         fstrcpy(name_list[ent->num_sam_entries + i].homedir,
638                                 info[i].homedir);
639                 }
640                 if (!info[i].shell) {
641                         fstrcpy(name_list[ent->num_sam_entries + i].shell, "");
642                 } else {
643                         fstrcpy(name_list[ent->num_sam_entries + i].shell,
644                                 info[i].shell);
645                 }
646
647
648                 /* User and group ids */
649                 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
650                          &info[i].user_sid);
651                 sid_copy(&name_list[ent->num_sam_entries+i].group_sid,
652                          &info[i].group_sid);
653         }
654
655         ent->num_sam_entries += num_entries;
656
657         /* Fill in remaining fields */
658
659         ent->sam_entries = name_list;
660         ent->sam_entry_index = 0;
661         return ent->num_sam_entries > 0;
662 }
663
664 /* Fetch next passwd entry from ntdom database */
665
666 #define MAX_GETPWENT_USERS 500
667
668 void winbindd_getpwent(struct winbindd_cli_state *state)
669 {
670         struct getent_state *ent;
671         struct winbindd_pw *user_list;
672         int num_users, user_list_ndx;
673
674         DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
675
676         /* Check user has enabled this */
677
678         if (!lp_winbind_enum_users()) {
679                 request_error(state);
680                 return;
681         }
682
683         /* Allocate space for returning a chunk of users */
684
685         num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
686
687         if (num_users == 0) {
688                 request_error(state);
689                 return;
690         }
691
692         user_list = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users);
693         if (!user_list) {
694                 request_error(state);
695                 return;
696         }
697         /* will be freed by process_request() */
698         state->response.extra_data.data = user_list;
699
700         memset(user_list, 0, num_users * sizeof(struct winbindd_pw));
701
702         if (!state->getpwent_initialized)
703                 winbindd_setpwent_internal(state);
704
705         if (!(ent = state->getpwent_state)) {
706                 request_error(state);
707                 return;
708         }
709
710         /* Start sending back users */
711
712         for (user_list_ndx = 0; user_list_ndx < num_users; ) {
713                 struct getpwent_user *name_list = NULL;
714                 uint32 result;
715
716                 /* Do we need to fetch another chunk of users? */
717
718                 if (ent->num_sam_entries == ent->sam_entry_index) {
719
720                         while(ent &&
721                               !get_sam_user_entries(ent, state->mem_ctx)) {
722                                 struct getent_state *next_ent;
723
724                                 /* Free state information for this domain */
725
726                                 SAFE_FREE(ent->sam_entries);
727
728                                 next_ent = ent->next;
729                                 DLIST_REMOVE(state->getpwent_state, ent);
730
731                                 SAFE_FREE(ent);
732                                 ent = next_ent;
733                         }
734
735                         /* No more domains */
736
737                         if (!ent)
738                                 break;
739                 }
740
741                 name_list = (struct getpwent_user *)ent->sam_entries;
742
743                 /* Lookup user info */
744
745                 result = winbindd_fill_pwent(
746                         ent->domain_name,
747                         name_list[ent->sam_entry_index].name,
748                         &name_list[ent->sam_entry_index].user_sid,
749                         &name_list[ent->sam_entry_index].group_sid,
750                         name_list[ent->sam_entry_index].gecos,
751                         name_list[ent->sam_entry_index].homedir,
752                         name_list[ent->sam_entry_index].shell,
753                         &user_list[user_list_ndx]);
754
755                 /* Add user to return list */
756
757                 if (result) {
758
759                         user_list_ndx++;
760                         state->response.data.num_entries++;
761                         state->response.length += sizeof(struct winbindd_pw);
762
763                 } else
764                         DEBUG(1, ("could not lookup domain user %s\n",
765                                   name_list[ent->sam_entry_index].name));
766
767                 ent->sam_entry_index++;
768
769         }
770
771         /* Out of domains */
772
773         if (user_list_ndx > 0)
774                 request_ok(state);
775         else
776                 request_error(state);
777 }
778
779 /* List domain users without mapping to unix ids */
780 void winbindd_list_users(struct winbindd_cli_state *state)
781 {
782         winbindd_list_ent(state, LIST_USERS);
783 }