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