r23779: Change from v2 or later to v3 or later.
[samba.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, 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, WINBINDD_GETPWNAM, 
404                                   state);
405 }
406
407 static void getpwnam_name2sid_recv(void *private_data, BOOL success,
408                                    const DOM_SID *sid, enum lsa_SidType type)
409 {
410         struct winbindd_cli_state *state =
411                 (struct winbindd_cli_state *)private_data;
412         fstring domname, username;      
413
414         if (!success) {
415                 DEBUG(5, ("Could not lookup name for user %s\n",
416                           state->request.data.username));
417                 request_error(state);
418                 return;
419         }
420
421         if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) {
422                 DEBUG(5, ("%s is not a user\n", state->request.data.username));
423                 request_error(state);
424                 return;
425         }
426
427         if ( parse_domain_user(state->request.data.username, domname, username) ) {
428                 check_domain_trusted( domname, sid );   
429         }
430
431
432
433         winbindd_getpwsid(state, sid);
434 }
435
436 static void getpwuid_recv(void *private_data, BOOL success, const char *sid)
437 {
438         struct winbindd_cli_state *state =
439                 (struct winbindd_cli_state *)private_data;
440         DOM_SID user_sid;
441
442         if (!success) {
443                 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
444                           (unsigned long)(state->request.data.uid)));
445                 request_error(state);
446                 return;
447         }
448         
449         DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
450                   (unsigned long)(state->request.data.uid), sid));
451
452         string_to_sid(&user_sid, sid);
453         winbindd_getpwsid(state, &user_sid);
454 }
455
456 /* Return a password structure given a uid number */
457 void winbindd_getpwuid(struct winbindd_cli_state *state)
458 {
459         DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state->pid, 
460                   (unsigned long)state->request.data.uid));
461
462         /* always query idmap via the async interface */
463         /* if this turns to be too slow we will add here a direct query to the cache */
464         winbindd_uid2sid_async(state->mem_ctx, state->request.data.uid, getpwuid_recv, state);
465 }
466
467 /*
468  * set/get/endpwent functions
469  */
470
471 /* Rewind file pointer for ntdom passwd database */
472
473 static BOOL winbindd_setpwent_internal(struct winbindd_cli_state *state)
474 {
475         struct winbindd_domain *domain;
476         
477         DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
478         
479         /* Check user has enabled this */
480         
481         if (!lp_winbind_enum_users()) {
482                 return False;
483         }
484
485         /* Free old static data if it exists */
486         
487         if (state->getpwent_state != NULL) {
488                 free_getent_state(state->getpwent_state);
489                 state->getpwent_state = NULL;
490         }
491
492 #if 0   /* JERRY */
493         /* add any local users we have */
494                 
495         if ( (domain_state = (struct getent_state *)malloc(sizeof(struct getent_state))) == NULL )
496                 return False;
497                 
498         ZERO_STRUCTP(domain_state);
499
500         /* Add to list of open domains */
501                 
502         DLIST_ADD(state->getpwent_state, domain_state);
503 #endif
504         
505         /* Create sam pipes for each domain we know about */
506         
507         for(domain = domain_list(); domain != NULL; domain = domain->next) {
508                 struct getent_state *domain_state;
509                 
510                 
511                 /* don't add our domaina if we are a PDC or if we 
512                    are a member of a Samba domain */
513                 
514                 if ( (IS_DC || lp_winbind_trusted_domains_only())
515                         && strequal(domain->name, lp_workgroup()) )
516                 {
517                         continue;
518                 }
519                                                 
520                 /* Create a state record for this domain */
521                 
522                 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
523                         DEBUG(0, ("malloc failed\n"));
524                         return False;
525                 }
526                 
527                 ZERO_STRUCTP(domain_state);
528
529                 fstrcpy(domain_state->domain_name, domain->name);
530
531                 /* Add to list of open domains */
532                 
533                 DLIST_ADD(state->getpwent_state, domain_state);
534         }
535         
536         state->getpwent_initialized = True;
537         return True;
538 }
539
540 void winbindd_setpwent(struct winbindd_cli_state *state)
541 {
542         if (winbindd_setpwent_internal(state)) {
543                 request_ok(state);
544         } else {
545                 request_error(state);
546         }
547 }
548
549 /* Close file pointer to ntdom passwd database */
550
551 void winbindd_endpwent(struct winbindd_cli_state *state)
552 {
553         DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
554
555         free_getent_state(state->getpwent_state);    
556         state->getpwent_initialized = False;
557         state->getpwent_state = NULL;
558         request_ok(state);
559 }
560
561 /* Get partial list of domain users for a domain.  We fill in the sam_entries,
562    and num_sam_entries fields with domain user information.  The dispinfo_ndx
563    field is incremented to the index of the next user to fetch.  Return True if
564    some users were returned, False otherwise. */
565
566 static BOOL get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
567 {
568         NTSTATUS status;
569         uint32 num_entries;
570         WINBIND_USERINFO *info;
571         struct getpwent_user *name_list = NULL;
572         struct winbindd_domain *domain;
573         struct winbindd_methods *methods;
574         unsigned int i;
575
576         if (ent->num_sam_entries)
577                 return False;
578
579         if (!(domain = find_domain_from_name(ent->domain_name))) {
580                 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
581                           ent->domain_name));
582                 return False;
583         }
584
585         methods = domain->methods;
586
587         /* Free any existing user info */
588
589         SAFE_FREE(ent->sam_entries);
590         ent->num_sam_entries = 0;
591         
592         /* Call query_user_list to get a list of usernames and user rids */
593
594         num_entries = 0;
595
596         status = methods->query_user_list(domain, mem_ctx, &num_entries, 
597                                           &info);
598                 
599         if (!NT_STATUS_IS_OK(status)) {
600                 DEBUG(10,("get_sam_user_entries: query_user_list failed with %s\n",
601                         nt_errstr(status) ));
602                 return False;
603         }
604
605         if (num_entries) {
606                 name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user, ent->num_sam_entries + num_entries);
607                 
608                 if (!name_list) {
609                         DEBUG(0,("get_sam_user_entries realloc failed.\n"));
610                         return False;
611                 }
612         }
613
614         for (i = 0; i < num_entries; i++) {
615                 /* Store account name and gecos */
616                 if (!info[i].acct_name) {
617                         fstrcpy(name_list[ent->num_sam_entries + i].name, "");
618                 } else {
619                         fstrcpy(name_list[ent->num_sam_entries + i].name, 
620                                 info[i].acct_name); 
621                 }
622                 if (!info[i].full_name) {
623                         fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
624                 } else {
625                         fstrcpy(name_list[ent->num_sam_entries + i].gecos, 
626                                 info[i].full_name); 
627                 }
628                 if (!info[i].homedir) {
629                         fstrcpy(name_list[ent->num_sam_entries + i].homedir, "");
630                 } else {
631                         fstrcpy(name_list[ent->num_sam_entries + i].homedir, 
632                                 info[i].homedir); 
633                 }
634                 if (!info[i].shell) {
635                         fstrcpy(name_list[ent->num_sam_entries + i].shell, "");
636                 } else {
637                         fstrcpy(name_list[ent->num_sam_entries + i].shell, 
638                                 info[i].shell); 
639                 }
640         
641         
642                 /* User and group ids */
643                 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
644                          &info[i].user_sid);
645                 sid_copy(&name_list[ent->num_sam_entries+i].group_sid,
646                          &info[i].group_sid);
647         }
648                 
649         ent->num_sam_entries += num_entries;
650         
651         /* Fill in remaining fields */
652         
653         ent->sam_entries = name_list;
654         ent->sam_entry_index = 0;
655         return ent->num_sam_entries > 0;
656 }
657
658 /* Fetch next passwd entry from ntdom database */
659
660 #define MAX_GETPWENT_USERS 500
661
662 void winbindd_getpwent(struct winbindd_cli_state *state)
663 {
664         struct getent_state *ent;
665         struct winbindd_pw *user_list;
666         int num_users, user_list_ndx;
667
668         DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
669
670         /* Check user has enabled this */
671
672         if (!lp_winbind_enum_users()) {
673                 request_error(state);
674                 return;
675         }
676
677         /* Allocate space for returning a chunk of users */
678
679         num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
680
681         if (num_users == 0) {
682                 request_error(state);
683                 return;
684         }
685         
686         if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users)) == NULL) {
687                 request_error(state);
688                 return;
689         }
690
691         memset(state->response.extra_data.data, 0, num_users * 
692                sizeof(struct winbindd_pw));
693
694         user_list = (struct winbindd_pw *)state->response.extra_data.data;
695
696         if (!state->getpwent_initialized)
697                 winbindd_setpwent_internal(state);
698         
699         if (!(ent = state->getpwent_state)) {
700                 request_error(state);
701                 return;
702         }
703
704         /* Start sending back users */
705
706         for (user_list_ndx = 0; user_list_ndx < num_users; ) {
707                 struct getpwent_user *name_list = NULL;
708                 uint32 result;
709
710                 /* Do we need to fetch another chunk of users? */
711
712                 if (ent->num_sam_entries == ent->sam_entry_index) {
713
714                         while(ent &&
715                               !get_sam_user_entries(ent, state->mem_ctx)) {
716                                 struct getent_state *next_ent;
717
718                                 /* Free state information for this domain */
719
720                                 SAFE_FREE(ent->sam_entries);
721
722                                 next_ent = ent->next;
723                                 DLIST_REMOVE(state->getpwent_state, ent);
724
725                                 SAFE_FREE(ent);
726                                 ent = next_ent;
727                         }
728  
729                         /* No more domains */
730
731                         if (!ent) 
732                                 break;
733                 }
734
735                 name_list = (struct getpwent_user *)ent->sam_entries;
736
737                 /* Lookup user info */
738                 
739                 result = winbindd_fill_pwent(
740                         ent->domain_name, 
741                         name_list[ent->sam_entry_index].name,
742                         &name_list[ent->sam_entry_index].user_sid,
743                         &name_list[ent->sam_entry_index].group_sid,
744                         name_list[ent->sam_entry_index].gecos,
745                         name_list[ent->sam_entry_index].homedir,
746                         name_list[ent->sam_entry_index].shell,
747                         &user_list[user_list_ndx]);
748                 
749                 /* Add user to return list */
750                 
751                 if (result) {
752                                 
753                         user_list_ndx++;
754                         state->response.data.num_entries++;
755                         state->response.length += 
756                                 sizeof(struct winbindd_pw);
757
758                 } else
759                         DEBUG(1, ("could not lookup domain user %s\n",
760                                   name_list[ent->sam_entry_index].name));
761
762                 ent->sam_entry_index++;
763                 
764         }
765
766         /* Out of domains */
767
768         if (user_list_ndx > 0)
769                 request_ok(state);
770         else
771                 request_error(state);
772 }
773
774 /* List domain users without mapping to unix ids */
775
776 void winbindd_list_users(struct winbindd_cli_state *state)
777 {
778         struct winbindd_domain *domain;
779         WINBIND_USERINFO *info;
780         const char *which_domain;
781         uint32 num_entries = 0, total_entries = 0;
782         char *extra_data = NULL;
783         int extra_data_len = 0;
784         enum winbindd_result rv = WINBINDD_ERROR;
785
786         DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid));
787
788         /* Ensure null termination */
789         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';  
790         which_domain = state->request.domain_name;
791         
792         /* Enumerate over trusted domains */
793
794         for (domain = domain_list(); domain; domain = domain->next) {
795                 NTSTATUS status;
796                 struct winbindd_methods *methods;
797                 unsigned int i;
798                 
799                 /* if we have a domain name restricting the request and this
800                    one in the list doesn't match, then just bypass the remainder
801                    of the loop */
802                    
803                 if ( *which_domain && !strequal(which_domain, domain->name) )
804                         continue;
805                         
806                 methods = domain->methods;
807
808                 /* Query display info */
809                 status = methods->query_user_list(domain, state->mem_ctx, 
810                                                   &num_entries, &info);
811
812                 if (!NT_STATUS_IS_OK(status)) {
813                         continue;
814                 }
815
816                 if (num_entries == 0)
817                         continue;
818
819                 /* Allocate some memory for extra data */
820                 total_entries += num_entries;
821                         
822                 extra_data = (char *)SMB_REALLOC(
823                         extra_data, sizeof(fstring) * total_entries);
824                         
825                 if (!extra_data) {
826                         DEBUG(0,("failed to enlarge buffer!\n"));
827                         goto done;
828                 }
829
830                 /* Pack user list into extra data fields */
831                         
832                 for (i = 0; i < num_entries; i++) {
833                         fstring acct_name, name;
834                         
835                         if (!info[i].acct_name) {
836                                 fstrcpy(acct_name, "");
837                         } else {
838                                 fstrcpy(acct_name, info[i].acct_name);
839                         }
840                         
841                         fill_domain_username(name, domain->name, acct_name, True);
842                         
843                                 /* Append to extra data */
844                         memcpy(&extra_data[extra_data_len], name, 
845                                strlen(name));
846                         extra_data_len += strlen(name);
847                         extra_data[extra_data_len++] = ',';
848                 }   
849         }
850
851         /* Assign extra_data fields in response structure */
852
853         if (extra_data) {
854                 extra_data[extra_data_len - 1] = '\0';
855                 state->response.extra_data.data = extra_data;
856                 state->response.length += extra_data_len;
857         }
858
859         /* No domains responded but that's still OK so don't return an
860            error. */
861
862         rv = WINBINDD_OK;
863
864  done:
865
866         if (rv == WINBINDD_OK)
867                 request_ok(state);
868         else
869                 request_error(state);
870 }