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