r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[bbaumbach/samba-autobuild/.git] / source3 / 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 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         fstring sid_string;
77         
78         if (!pw || !dom_name || !user_name)
79                 return False;
80         
81         /* Resolve the uid number */
82
83         if (!NT_STATUS_IS_OK(idmap_sid_to_uid(user_sid, &pw->pw_uid))) {
84                 DEBUG(1, ("error getting user id for sid %s\n", sid_to_string(sid_string, user_sid)));
85                 return False;
86         }
87         
88         /* Resolve the gid number */   
89
90         if (!NT_STATUS_IS_OK(idmap_sid_to_gid(group_sid, &pw->pw_gid))) {
91                 DEBUG(1, ("error getting group id for sid %s\n", sid_to_string(sid_string, group_sid)));
92                 return False;
93         }
94
95         strlower_m(user_name);
96
97         /* Username */
98
99         fill_domain_username(output_username, dom_name, user_name, True); 
100
101         safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
102         
103         /* Full name (gecos) */
104         
105         safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
106
107         /* Home directory and shell */
108         
109         if (!fillup_pw_field(lp_template_homedir(), user_name, dom_name, 
110                              pw->pw_uid, pw->pw_gid, homedir, pw->pw_dir))
111                 return False;
112
113         if (!fillup_pw_field(lp_template_shell(), user_name, dom_name, 
114                              pw->pw_uid, pw->pw_gid, shell, pw->pw_shell))
115                 return False;
116
117         /* Password - set to "*" as we can't generate anything useful here.
118            Authentication can be done using the pam_winbind module. */
119
120         safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
121
122         return True;
123 }
124
125 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
126
127 enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain,
128                                             struct winbindd_cli_state *state)
129 {
130         DOM_SID sid;
131         WINBIND_USERINFO user_info;
132         NTSTATUS status;
133
134         /* Ensure null termination */
135         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
136
137         DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid, 
138                   state->request.data.sid));
139
140         if (!string_to_sid(&sid, state->request.data.sid)) {
141                 DEBUG(5, ("%s not a SID\n", state->request.data.sid));
142                 return WINBINDD_ERROR;
143         }
144
145         status = domain->methods->query_user(domain, state->mem_ctx,
146                                              &sid, &user_info);
147         if (!NT_STATUS_IS_OK(status)) {
148                 DEBUG(1, ("error getting user info for sid %s\n",
149                           sid_string_static(&sid)));
150                 return WINBINDD_ERROR;
151         }
152
153         fstrcpy(state->response.data.user_info.acct_name, user_info.acct_name);
154         fstrcpy(state->response.data.user_info.full_name, user_info.full_name);
155         fstrcpy(state->response.data.user_info.homedir, user_info.homedir);
156         fstrcpy(state->response.data.user_info.shell, user_info.shell);
157         state->response.data.user_info.primary_gid = user_info.primary_gid;     
158         if (!sid_peek_check_rid(&domain->sid, &user_info.group_sid,
159                                 &state->response.data.user_info.group_rid)) {
160                 DEBUG(1, ("Could not extract group rid out of %s\n",
161                           sid_string_static(&sid)));
162                 return WINBINDD_ERROR;
163         }
164
165         return WINBINDD_OK;
166 }
167
168 struct getpwsid_state {
169         struct winbindd_cli_state *state;
170         struct winbindd_domain *domain;
171         char *username;
172         char *fullname;
173         char *homedir;
174         char *shell;
175         DOM_SID user_sid;
176         uid_t uid;
177         DOM_SID group_sid;
178         gid_t gid;
179 };
180
181 static void getpwsid_queryuser_recv(void *private_data, BOOL success,
182                                     const char *acct_name,
183                                     const char *full_name, 
184                                     const char *homedir,
185                                     const char *shell,
186                                     uint32 gid,
187                                     uint32 group_rid);
188 static void getpwsid_sid2uid_recv(void *private_data, BOOL success, uid_t uid);
189 static void getpwsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid);
190
191 static void winbindd_getpwsid(struct winbindd_cli_state *state,
192                               const DOM_SID *sid)
193 {
194         struct getpwsid_state *s;
195
196         s = TALLOC_ZERO_P(state->mem_ctx, struct getpwsid_state);
197         if (s == NULL) {
198                 DEBUG(0, ("talloc failed\n"));
199                 goto error;
200         }
201
202         s->state = state;
203         s->domain = find_domain_from_sid_noinit(sid);
204         if (s->domain == NULL) {
205                 DEBUG(3, ("Could not find domain for sid %s\n",
206                           sid_string_static(sid)));
207                 goto error;
208         }
209
210         sid_copy(&s->user_sid, sid);
211
212         query_user_async(s->state->mem_ctx, s->domain, sid,
213                          getpwsid_queryuser_recv, s);
214         return;
215
216  error:
217         request_error(state);
218 }
219         
220 static void getpwsid_queryuser_recv(void *private_data, BOOL success,
221                                     const char *acct_name,
222                                     const char *full_name, 
223                                     const char *homedir,
224                                     const char *shell,
225                                     uint32 gid,
226                                     uint32 group_rid)
227 {
228         fstring username;
229         struct getpwsid_state *s =
230                 talloc_get_type_abort(private_data, struct getpwsid_state);
231
232         if (!success) {
233                 DEBUG(5, ("Could not query domain %s SID %s\n", s->domain->name,
234                           sid_string_static(&s->user_sid)));
235                 request_error(s->state);
236                 return;
237         }
238
239         if ( acct_name && *acct_name ) {
240         fstrcpy( username, acct_name );
241         } else {                
242                 char *domain_name = NULL;
243                 enum lsa_SidType type;
244                 char *user_name = NULL;
245                 struct winbindd_domain *domain = NULL;
246                 
247                 domain = find_lookup_domain_from_sid(&s->user_sid);
248                 winbindd_lookup_name_by_sid(s->state->mem_ctx, domain,
249                                             &s->user_sid, &domain_name,
250                                             &user_name, &type );                
251
252                 /* If this still fails we ar4e done.  Just error out */
253                 if ( !user_name ) {
254                         DEBUG(5,("Could not obtain a name for SID %s\n",
255                                  sid_string_static(&s->user_sid)));                     
256                         request_error(s->state);
257                         return;                 
258                 }
259
260                 fstrcpy( username, user_name );         
261         }
262
263         strlower_m( username );
264         s->username = talloc_strdup(s->state->mem_ctx, username);
265
266         ws_name_replace( s->username, WB_REPLACE_CHAR );
267          
268         s->fullname = talloc_strdup(s->state->mem_ctx, full_name);
269         s->homedir = talloc_strdup(s->state->mem_ctx, homedir);
270         s->shell = talloc_strdup(s->state->mem_ctx, shell);
271         s->gid = gid;   
272         sid_copy(&s->group_sid, &s->domain->sid);
273         sid_append_rid(&s->group_sid, group_rid);
274
275         winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid,
276                                getpwsid_sid2uid_recv, s);
277 }
278
279 static void getpwsid_sid2uid_recv(void *private_data, BOOL success, uid_t uid)
280 {
281         struct getpwsid_state *s =
282                 talloc_get_type_abort(private_data, struct getpwsid_state);
283
284         if (!success) {
285                 DEBUG(5, ("Could not query uid for user %s\\%s\n",
286                           s->domain->name, s->username));
287                 request_error(s->state);
288                 return;
289         }
290
291         s->uid = uid;
292         winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
293                                getpwsid_sid2gid_recv, s);
294 }
295
296 static void getpwsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
297 {
298         struct getpwsid_state *s =
299                 talloc_get_type_abort(private_data, struct getpwsid_state);
300         struct winbindd_pw *pw;
301         fstring output_username;
302
303         /* allow the nss backend to override the primary group ID.
304            If the gid has already been set, then keep it.
305            This makes me feel dirty.  If the nss backend already
306            gave us a gid, we don't really care whether the sid2gid()
307            call worked or not.   --jerry  */
308
309         if ( s->gid == (gid_t)-1 ) {
310
311                 if (!success) {
312                         DEBUG(5, ("Could not query gid for user %s\\%s\n",
313                                   s->domain->name, s->username));
314                         goto failed;
315                 }
316
317                 /* take what the sid2gid() call gave us */
318                 s->gid = gid;
319         }
320
321         pw = &s->state->response.data.pw;
322         pw->pw_uid = s->uid;
323         pw->pw_gid = s->gid;
324         fill_domain_username(output_username, s->domain->name, s->username, True); 
325         safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
326         safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1);
327
328         if (!fillup_pw_field(lp_template_homedir(), s->username, s->domain->name, 
329                              pw->pw_uid, pw->pw_gid, s->homedir, pw->pw_dir)) {
330                 DEBUG(5, ("Could not compose homedir\n"));
331                 goto failed;
332         }
333
334         if (!fillup_pw_field(lp_template_shell(), s->username, s->domain->name, 
335                              pw->pw_uid, pw->pw_gid, s->shell, pw->pw_shell)) {
336                 DEBUG(5, ("Could not compose shell\n"));
337                 goto failed;
338         }
339
340         /* Password - set to "*" as we can't generate anything useful here.
341            Authentication can be done using the pam_winbind module. */
342
343         safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
344
345         request_ok(s->state);
346         return;
347
348  failed:
349         request_error(s->state);
350 }
351
352 /* Return a password structure from a username.  */
353
354 static void getpwnam_name2sid_recv(void *private_data, BOOL success,
355                                    const DOM_SID *sid, enum lsa_SidType type);
356
357 void winbindd_getpwnam(struct winbindd_cli_state *state)
358 {
359         struct winbindd_domain *domain;
360         fstring domname, username;
361
362         /* Ensure null termination */
363         state->request.data.username[sizeof(state->request.data.username)-1]='\0';
364
365         DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state->pid,
366                   state->request.data.username));
367
368         ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
369
370         if (!parse_domain_user(state->request.data.username, domname,
371                                username)) {
372                 DEBUG(5, ("Could not parse domain user: %s\n",
373                           state->request.data.username));
374                 request_error(state);
375                 return;
376         }
377         
378         /* Get info for the domain */
379
380         domain = find_domain_from_name(domname);
381
382         if (domain == NULL) {
383                 DEBUG(7, ("could not find domain entry for domain %s.  "
384                           "Using primary domain\n", domname));
385                 if ( (domain = find_our_domain()) == NULL ) {
386                         DEBUG(0,("Cannot find my primary domain structure!\n"));
387                 request_error(state);
388                 return;
389         }
390         }
391
392         if ( strequal(domname, lp_workgroup()) && lp_winbind_trusted_domains_only() ) {
393                 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getpwnam() for %s\\%s.\n", 
394                         domname, username));
395                 request_error(state);
396                 return;
397         }       
398
399         /* Get rid and name type from name.  The following costs 1 packet */
400
401         winbindd_lookupname_async(state->mem_ctx, domname, username,
402                                   getpwnam_name2sid_recv, WINBINDD_GETPWNAM, 
403                                   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;
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 (num_users == 0) {
681                 request_error(state);
682                 return;
683         }
684         
685         if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users)) == NULL) {
686                 request_error(state);
687                 return;
688         }
689
690         memset(state->response.extra_data.data, 0, num_users * 
691                sizeof(struct winbindd_pw));
692
693         user_list = (struct winbindd_pw *)state->response.extra_data.data;
694
695         if (!state->getpwent_initialized)
696                 winbindd_setpwent_internal(state);
697         
698         if (!(ent = state->getpwent_state)) {
699                 request_error(state);
700                 return;
701         }
702
703         /* Start sending back users */
704
705         for (user_list_ndx = 0; user_list_ndx < num_users; ) {
706                 struct getpwent_user *name_list = NULL;
707                 uint32 result;
708
709                 /* Do we need to fetch another chunk of users? */
710
711                 if (ent->num_sam_entries == ent->sam_entry_index) {
712
713                         while(ent &&
714                               !get_sam_user_entries(ent, state->mem_ctx)) {
715                                 struct getent_state *next_ent;
716
717                                 /* Free state information for this domain */
718
719                                 SAFE_FREE(ent->sam_entries);
720
721                                 next_ent = ent->next;
722                                 DLIST_REMOVE(state->getpwent_state, ent);
723
724                                 SAFE_FREE(ent);
725                                 ent = next_ent;
726                         }
727  
728                         /* No more domains */
729
730                         if (!ent) 
731                                 break;
732                 }
733
734                 name_list = (struct getpwent_user *)ent->sam_entries;
735
736                 /* Lookup user info */
737                 
738                 result = winbindd_fill_pwent(
739                         ent->domain_name, 
740                         name_list[ent->sam_entry_index].name,
741                         &name_list[ent->sam_entry_index].user_sid,
742                         &name_list[ent->sam_entry_index].group_sid,
743                         name_list[ent->sam_entry_index].gecos,
744                         name_list[ent->sam_entry_index].homedir,
745                         name_list[ent->sam_entry_index].shell,
746                         &user_list[user_list_ndx]);
747                 
748                 /* Add user to return list */
749                 
750                 if (result) {
751                                 
752                         user_list_ndx++;
753                         state->response.data.num_entries++;
754                         state->response.length += 
755                                 sizeof(struct winbindd_pw);
756
757                 } else
758                         DEBUG(1, ("could not lookup domain user %s\n",
759                                   name_list[ent->sam_entry_index].name));
760
761                 ent->sam_entry_index++;
762                 
763         }
764
765         /* Out of domains */
766
767         if (user_list_ndx > 0)
768                 request_ok(state);
769         else
770                 request_error(state);
771 }
772
773 /* List domain users without mapping to unix ids */
774
775 void winbindd_list_users(struct winbindd_cli_state *state)
776 {
777         struct winbindd_domain *domain;
778         WINBIND_USERINFO *info;
779         const char *which_domain;
780         uint32 num_entries = 0, total_entries = 0;
781         char *extra_data = NULL;
782         int extra_data_len = 0;
783         enum winbindd_result rv = WINBINDD_ERROR;
784
785         DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid));
786
787         /* Ensure null termination */
788         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';  
789         which_domain = state->request.domain_name;
790         
791         /* Enumerate over trusted domains */
792
793         for (domain = domain_list(); domain; domain = domain->next) {
794                 NTSTATUS status;
795                 struct winbindd_methods *methods;
796                 unsigned int i;
797                 
798                 /* if we have a domain name restricting the request and this
799                    one in the list doesn't match, then just bypass the remainder
800                    of the loop */
801                    
802                 if ( *which_domain && !strequal(which_domain, domain->name) )
803                         continue;
804                         
805                 methods = domain->methods;
806
807                 /* Query display info */
808                 status = methods->query_user_list(domain, state->mem_ctx, 
809                                                   &num_entries, &info);
810
811                 if (!NT_STATUS_IS_OK(status)) {
812                         continue;
813                 }
814
815                 if (num_entries == 0)
816                         continue;
817
818                 /* Allocate some memory for extra data */
819                 total_entries += num_entries;
820                         
821                 extra_data = (char *)SMB_REALLOC(
822                         extra_data, sizeof(fstring) * total_entries);
823                         
824                 if (!extra_data) {
825                         DEBUG(0,("failed to enlarge buffer!\n"));
826                         goto done;
827                 }
828
829                 /* Pack user list into extra data fields */
830                         
831                 for (i = 0; i < num_entries; i++) {
832                         fstring acct_name, name;
833                         
834                         if (!info[i].acct_name) {
835                                 fstrcpy(acct_name, "");
836                         } else {
837                                 fstrcpy(acct_name, info[i].acct_name);
838                         }
839                         
840                         fill_domain_username(name, domain->name, acct_name, True);
841                         
842                                 /* Append to extra data */
843                         memcpy(&extra_data[extra_data_len], name, 
844                                strlen(name));
845                         extra_data_len += strlen(name);
846                         extra_data[extra_data_len++] = ',';
847                 }   
848         }
849
850         /* Assign extra_data fields in response structure */
851
852         if (extra_data) {
853                 extra_data[extra_data_len - 1] = '\0';
854                 state->response.extra_data.data = extra_data;
855                 state->response.length += extra_data_len;
856         }
857
858         /* No domains responded but that's still OK so don't return an
859            error. */
860
861         rv = WINBINDD_OK;
862
863  done:
864
865         if (rv == WINBINDD_OK)
866                 request_ok(state);
867         else
868                 request_error(state);
869 }