s3:winbind_user: fix "getent passwd" to allocate new uids.
[Samba/bb.git] / source / winbindd / winbindd_user.c
blobb01e1847f26369d5a3b14735a8bed2b4a34882ee
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon - user related functions
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jeremy Allison 2001.
8 Copyright (C) Gerald (Jerry) Carter 2003.
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.
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.
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/>.
24 #include "includes.h"
25 #include "winbindd.h"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
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)
38 char *templ;
40 if (out == NULL)
41 return False;
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. */
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);
59 if (!templ)
60 return False;
62 safe_strcpy(out, templ, sizeof(fstring) - 1);
63 TALLOC_FREE(templ);
65 return True;
68 /* Fill a pwent structure with information we have obtained */
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)
75 fstring output_username;
76 char *mapped_name = NULL;
77 struct winbindd_domain *domain = NULL;
78 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
79 char *dom_name_idmap = "";
81 if (!pw || !dom_name || !user_name)
82 return False;
84 domain = find_domain_from_name_noinit(dom_name);
85 if (domain == NULL) {
86 DEBUG(5,("winbindd_fill_pwent: Failed to find domain for %s. "
87 "Disabling name alias support\n", dom_name));
88 nt_status = NT_STATUS_NO_SUCH_DOMAIN;
91 if (domain->have_idmap_config) {
92 dom_name_idmap = dom_name;
95 /* Resolve the uid number */
97 if (!NT_STATUS_IS_OK(idmap_sid_to_uid(dom_name_idmap, user_sid,
98 &pw->pw_uid))) {
99 DEBUG(1, ("error getting user id for sid %s\n",
100 sid_string_dbg(user_sid)));
101 return False;
104 /* Resolve the gid number */
106 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(dom_name_idmap, group_sid,
107 &pw->pw_gid))) {
108 DEBUG(1, ("error getting group id for sid %s\n",
109 sid_string_dbg(group_sid)));
110 return False;
113 /* Username */
115 strlower_m(user_name);
116 nt_status = normalize_name_map(ctx, domain, user_name, &mapped_name);
118 /* Basic removal of whitespace */
119 if (NT_STATUS_IS_OK(nt_status)) {
120 fill_domain_username(output_username, dom_name, mapped_name, True);
122 /* Complete name replacement */
123 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
124 fstrcpy(output_username, mapped_name);
126 /* No change at all */
127 else {
128 fill_domain_username(output_username, dom_name, user_name, True);
131 safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
133 /* Full name (gecos) */
135 safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
137 /* Home directory and shell */
139 if (!fillup_pw_field(lp_template_homedir(), user_name, dom_name,
140 pw->pw_uid, pw->pw_gid, homedir, pw->pw_dir))
141 return False;
143 if (!fillup_pw_field(lp_template_shell(), user_name, dom_name,
144 pw->pw_uid, pw->pw_gid, shell, pw->pw_shell))
145 return False;
147 /* Password - set to "*" as we can't generate anything useful here.
148 Authentication can be done using the pam_winbind module. */
150 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
152 return True;
155 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
157 enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain,
158 struct winbindd_cli_state *state)
160 DOM_SID sid;
161 WINBIND_USERINFO user_info;
162 NTSTATUS status;
164 /* Ensure null termination */
165 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
167 DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid,
168 state->request.data.sid));
170 if (!string_to_sid(&sid, state->request.data.sid)) {
171 DEBUG(5, ("%s not a SID\n", state->request.data.sid));
172 return WINBINDD_ERROR;
175 status = domain->methods->query_user(domain, state->mem_ctx,
176 &sid, &user_info);
177 if (!NT_STATUS_IS_OK(status)) {
178 DEBUG(1, ("error getting user info for sid %s\n",
179 sid_string_dbg(&sid)));
180 return WINBINDD_ERROR;
183 fstrcpy(state->response.data.user_info.acct_name, user_info.acct_name);
184 fstrcpy(state->response.data.user_info.full_name, user_info.full_name);
185 fstrcpy(state->response.data.user_info.homedir, user_info.homedir);
186 fstrcpy(state->response.data.user_info.shell, user_info.shell);
187 state->response.data.user_info.primary_gid = user_info.primary_gid;
188 if (!sid_peek_check_rid(&domain->sid, &user_info.group_sid,
189 &state->response.data.user_info.group_rid)) {
190 DEBUG(1, ("Could not extract group rid out of %s\n",
191 sid_string_dbg(&sid)));
192 return WINBINDD_ERROR;
195 return WINBINDD_OK;
198 struct getpwsid_state {
199 struct winbindd_cli_state *state;
200 struct winbindd_domain *domain;
201 char *username;
202 char *fullname;
203 char *homedir;
204 char *shell;
205 DOM_SID user_sid;
206 uid_t uid;
207 DOM_SID group_sid;
208 gid_t gid;
209 bool username_mapped;
212 static void getpwsid_queryuser_recv(void *private_data, bool success,
213 const char *acct_name,
214 const char *full_name,
215 const char *homedir,
216 const char *shell,
217 gid_t gid,
218 uint32 group_rid);
219 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid);
220 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid);
222 static void winbindd_getpwsid(struct winbindd_cli_state *state,
223 const DOM_SID *sid)
225 struct getpwsid_state *s;
227 s = TALLOC_ZERO_P(state->mem_ctx, struct getpwsid_state);
228 if (s == NULL) {
229 DEBUG(0, ("talloc failed\n"));
230 goto error;
233 s->state = state;
234 s->domain = find_domain_from_sid_noinit(sid);
235 if (s->domain == NULL) {
236 DEBUG(3, ("Could not find domain for sid %s\n",
237 sid_string_dbg(sid)));
238 goto error;
241 sid_copy(&s->user_sid, sid);
243 query_user_async(s->state->mem_ctx, s->domain, sid,
244 getpwsid_queryuser_recv, s);
245 return;
247 error:
248 request_error(state);
251 static void getpwsid_queryuser_recv(void *private_data, bool success,
252 const char *acct_name,
253 const char *full_name,
254 const char *homedir,
255 const char *shell,
256 gid_t gid,
257 uint32 group_rid)
259 fstring username;
260 struct getpwsid_state *s =
261 talloc_get_type_abort(private_data, struct getpwsid_state);
262 char *mapped_name;
263 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
265 if (!success) {
266 DEBUG(5, ("Could not query domain %s SID %s\n",
267 s->domain->name, sid_string_dbg(&s->user_sid)));
268 request_error(s->state);
269 return;
272 if ( acct_name && *acct_name ) {
273 fstrcpy( username, acct_name );
274 } else {
275 char *domain_name = NULL;
276 enum lsa_SidType type;
277 char *user_name = NULL;
278 struct winbindd_domain *domain = NULL;
280 domain = find_lookup_domain_from_sid(&s->user_sid);
281 if (domain == NULL) {
282 DEBUG(5, ("find_lookup_domain_from_sid(%s) failed\n",
283 sid_string_dbg(&s->user_sid)));
284 request_error(s->state);
285 return;
287 winbindd_lookup_name_by_sid(s->state->mem_ctx, domain,
288 &s->user_sid, &domain_name,
289 &user_name, &type );
291 /* If this still fails we ar4e done. Just error out */
292 if ( !user_name ) {
293 DEBUG(5,("Could not obtain a name for SID %s\n",
294 sid_string_dbg(&s->user_sid)));
295 request_error(s->state);
296 return;
299 fstrcpy( username, user_name );
302 strlower_m( username );
303 s->username = talloc_strdup(s->state->mem_ctx, username);
305 nt_status = normalize_name_map(s->state->mem_ctx, s->domain,
306 s->username, &mapped_name);
308 /* Basic removal of whitespace */
309 if (NT_STATUS_IS_OK(nt_status)) {
310 s->username = mapped_name;
311 s->username_mapped = false;
313 /* Complete name replacement */
314 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
315 s->username = mapped_name;
316 s->username_mapped = true;
318 /* No change at all */
319 else {
320 s->username_mapped = false;
323 s->fullname = talloc_strdup(s->state->mem_ctx, full_name);
324 s->homedir = talloc_strdup(s->state->mem_ctx, homedir);
325 s->shell = talloc_strdup(s->state->mem_ctx, shell);
326 s->gid = gid;
327 sid_copy(&s->group_sid, &s->domain->sid);
328 sid_append_rid(&s->group_sid, group_rid);
330 winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid,
331 getpwsid_sid2uid_recv, s);
334 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid)
336 struct getpwsid_state *s =
337 talloc_get_type_abort(private_data, struct getpwsid_state);
339 if (!success) {
340 DEBUG(5, ("Could not query uid for user %s\\%s\n",
341 s->domain->name, s->username));
342 request_error(s->state);
343 return;
346 s->uid = uid;
347 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
348 getpwsid_sid2gid_recv, s);
351 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
353 struct getpwsid_state *s =
354 talloc_get_type_abort(private_data, struct getpwsid_state);
355 struct winbindd_pw *pw;
356 fstring output_username;
358 /* allow the nss backend to override the primary group ID.
359 If the gid has already been set, then keep it.
360 This makes me feel dirty. If the nss backend already
361 gave us a gid, we don't really care whether the sid2gid()
362 call worked or not. --jerry */
364 if ( s->gid == (gid_t)-1 ) {
366 if (!success) {
367 DEBUG(5, ("Could not query gid for user %s\\%s\n",
368 s->domain->name, s->username));
369 goto failed;
372 /* take what the sid2gid() call gave us */
373 s->gid = gid;
376 pw = &s->state->response.data.pw;
377 pw->pw_uid = s->uid;
378 pw->pw_gid = s->gid;
380 /* allow username to be overridden by the alias mapping */
382 if ( s->username_mapped ) {
383 fstrcpy( output_username, s->username );
384 } else {
385 fill_domain_username(output_username, s->domain->name,
386 s->username, True);
389 safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
390 safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1);
392 if (!fillup_pw_field(lp_template_homedir(), s->username,
393 s->domain->name, pw->pw_uid, pw->pw_gid,
394 s->homedir, pw->pw_dir)) {
395 DEBUG(5, ("Could not compose homedir\n"));
396 goto failed;
399 if (!fillup_pw_field(lp_template_shell(), s->username,
400 s->domain->name, pw->pw_uid, pw->pw_gid,
401 s->shell, pw->pw_shell)) {
402 DEBUG(5, ("Could not compose shell\n"));
403 goto failed;
406 /* Password - set to "*" as we can't generate anything useful here.
407 Authentication can be done using the pam_winbind module. */
409 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
411 request_ok(s->state);
412 return;
414 failed:
415 request_error(s->state);
418 /* Return a password structure from a username. */
420 static void getpwnam_name2sid_recv(void *private_data, bool success,
421 const DOM_SID *sid, enum lsa_SidType type);
423 void winbindd_getpwnam(struct winbindd_cli_state *state)
425 struct winbindd_domain *domain;
426 fstring domname, username;
427 char *mapped_user = NULL;
428 char *domuser;
429 size_t dusize;
430 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
432 domuser = state->request.data.username;
433 dusize = sizeof(state->request.data.username);
435 /* Ensure null termination (it's an fstring) */
436 domuser[dusize-1] = '\0';
438 DEBUG(3, ("[%5lu]: getpwnam %s\n",
439 (unsigned long)state->pid,
440 domuser));
442 nt_status = normalize_name_unmap(state->mem_ctx, domuser,
443 &mapped_user);
445 /* If we could not convert from an aliased name or a
446 normalized name, then just use the original name */
448 if (!NT_STATUS_IS_OK(nt_status) &&
449 !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
451 mapped_user = domuser;
454 if (!parse_domain_user(mapped_user, domname, username)) {
455 DEBUG(5, ("Could not parse domain user: %s\n", domuser));
456 request_error(state);
457 return;
460 /* Get info for the domain */
462 domain = find_domain_from_name_noinit(domname);
464 if (domain == NULL) {
465 DEBUG(7, ("could not find domain entry for domain %s. "
466 "Using primary domain\n", domname));
467 if ( (domain = find_our_domain()) == NULL ) {
468 DEBUG(0,("Cannot find my primary domain structure!\n"));
469 request_error(state);
470 return;
474 if (strequal(domname, lp_workgroup()) &&
475 lp_winbind_trusted_domains_only() ) {
476 DEBUG(7,("winbindd_getpwnam: My domain -- "
477 "rejecting getpwnam() for %s\\%s.\n",
478 domname, username));
479 request_error(state);
480 return;
483 /* Get rid and name type from name. The following costs 1 packet */
485 winbindd_lookupname_async(state->mem_ctx, domname, username,
486 getpwnam_name2sid_recv, WINBINDD_GETPWNAM,
487 state);
490 static void getpwnam_name2sid_recv(void *private_data, bool success,
491 const DOM_SID *sid, enum lsa_SidType type)
493 struct winbindd_cli_state *state =
494 (struct winbindd_cli_state *)private_data;
495 fstring domname, username;
496 char *domuser = state->request.data.username;
498 if (!success) {
499 DEBUG(5, ("Could not lookup name for user %s\n", domuser));
500 request_error(state);
501 return;
504 if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) {
505 DEBUG(5, ("%s is not a user\n", domuser));
506 request_error(state);
507 return;
510 if (parse_domain_user(domuser, domname, username)) {
511 check_domain_trusted(domname, sid);
514 winbindd_getpwsid(state, sid);
517 static void getpwuid_recv(void *private_data, bool success, const char *sid)
519 struct winbindd_cli_state *state =
520 (struct winbindd_cli_state *)private_data;
521 DOM_SID user_sid;
523 if (!success) {
524 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
525 (unsigned long)(state->request.data.uid)));
526 request_error(state);
527 return;
530 DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
531 (unsigned long)(state->request.data.uid), sid));
533 if (!string_to_sid(&user_sid, sid)) {
534 DEBUG(1,("uid2sid_recv: Could not convert sid %s "
535 "from string\n,", sid));
536 request_error(state);
537 return;
540 winbindd_getpwsid(state, &user_sid);
543 /* Return a password structure given a uid number */
544 void winbindd_getpwuid(struct winbindd_cli_state *state)
546 uid_t uid = state->request.data.uid;
548 DEBUG(3, ("[%5lu]: getpwuid %lu\n",
549 (unsigned long)state->pid,
550 (unsigned long)uid));
552 /* always query idmap via the async interface */
553 /* if this turns to be too slow we will add here
554 * a direct query to the cache */
555 winbindd_uid2sid_async(state->mem_ctx, uid, getpwuid_recv, state);
559 * set/get/endpwent functions
562 /* Rewind file pointer for ntdom passwd database */
564 static bool winbindd_setpwent_internal(struct winbindd_cli_state *state)
566 struct winbindd_domain *domain;
568 DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
570 /* Check user has enabled this */
572 if (!lp_winbind_enum_users()) {
573 return False;
576 /* Free old static data if it exists */
578 if (state->getpwent_state != NULL) {
579 free_getent_state(state->getpwent_state);
580 state->getpwent_state = NULL;
583 /* Create sam pipes for each domain we know about */
585 for(domain = domain_list(); domain != NULL; domain = domain->next) {
586 struct getent_state *domain_state;
589 /* don't add our domaina if we are a PDC or if we
590 are a member of a Samba domain */
592 if ((IS_DC || lp_winbind_trusted_domains_only())
593 && strequal(domain->name, lp_workgroup())) {
594 continue;
597 /* Create a state record for this domain */
599 domain_state = SMB_MALLOC_P(struct getent_state);
600 if (!domain_state) {
601 DEBUG(0, ("malloc failed\n"));
602 return False;
605 ZERO_STRUCTP(domain_state);
607 fstrcpy(domain_state->domain_name, domain->name);
609 /* Add to list of open domains */
611 DLIST_ADD(state->getpwent_state, domain_state);
614 state->getpwent_initialized = True;
615 return True;
618 void winbindd_setpwent(struct winbindd_cli_state *state)
620 if (winbindd_setpwent_internal(state)) {
621 request_ok(state);
622 } else {
623 request_error(state);
627 /* Close file pointer to ntdom passwd database */
629 void winbindd_endpwent(struct winbindd_cli_state *state)
631 DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
633 free_getent_state(state->getpwent_state);
634 state->getpwent_initialized = False;
635 state->getpwent_state = NULL;
636 request_ok(state);
639 /* Get partial list of domain users for a domain. We fill in the sam_entries,
640 and num_sam_entries fields with domain user information. The dispinfo_ndx
641 field is incremented to the index of the next user to fetch. Return True if
642 some users were returned, False otherwise. */
644 static bool get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
646 NTSTATUS status;
647 uint32 num_entries;
648 WINBIND_USERINFO *info;
649 struct getpwent_user *name_list = NULL;
650 struct winbindd_domain *domain;
651 struct winbindd_methods *methods;
652 unsigned int i;
654 if (ent->num_sam_entries)
655 return False;
657 if (!(domain = find_domain_from_name(ent->domain_name))) {
658 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
659 ent->domain_name));
660 return False;
663 methods = domain->methods;
665 /* Free any existing user info */
667 SAFE_FREE(ent->sam_entries);
668 ent->num_sam_entries = 0;
670 /* Call query_user_list to get a list of usernames and user rids */
672 num_entries = 0;
674 status = methods->query_user_list(domain, mem_ctx, &num_entries, &info);
676 if (!NT_STATUS_IS_OK(status)) {
677 DEBUG(10,("get_sam_user_entries: "
678 "query_user_list failed with %s\n",
679 nt_errstr(status)));
680 return False;
683 if (num_entries) {
684 name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user,
685 ent->num_sam_entries + num_entries);
686 if (!name_list) {
687 DEBUG(0,("get_sam_user_entries realloc failed.\n"));
688 return False;
692 for (i = 0; i < num_entries; i++) {
693 /* Store account name and gecos */
694 if (!info[i].acct_name) {
695 fstrcpy(name_list[ent->num_sam_entries + i].name, "");
696 } else {
697 fstrcpy(name_list[ent->num_sam_entries + i].name,
698 info[i].acct_name);
700 if (!info[i].full_name) {
701 fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
702 } else {
703 fstrcpy(name_list[ent->num_sam_entries + i].gecos,
704 info[i].full_name);
706 if (!info[i].homedir) {
707 fstrcpy(name_list[ent->num_sam_entries + i].homedir,"");
708 } else {
709 fstrcpy(name_list[ent->num_sam_entries + i].homedir,
710 info[i].homedir);
712 if (!info[i].shell) {
713 fstrcpy(name_list[ent->num_sam_entries + i].shell, "");
714 } else {
715 fstrcpy(name_list[ent->num_sam_entries + i].shell,
716 info[i].shell);
720 /* User and group ids */
721 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
722 &info[i].user_sid);
723 sid_copy(&name_list[ent->num_sam_entries+i].group_sid,
724 &info[i].group_sid);
727 ent->num_sam_entries += num_entries;
729 /* Fill in remaining fields */
731 ent->sam_entries = name_list;
732 ent->sam_entry_index = 0;
733 return ent->num_sam_entries > 0;
736 /* Fetch next passwd entry from ntdom database */
738 #define MAX_GETPWENT_USERS 500
740 void winbindd_getpwent(struct winbindd_cli_state *state)
742 struct getent_state *ent;
743 struct winbindd_pw *user_list;
744 int num_users, user_list_ndx;
746 DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
748 /* Check user has enabled this */
750 if (!lp_winbind_enum_users()) {
751 request_error(state);
752 return;
755 /* Allocate space for returning a chunk of users */
757 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
759 if (num_users == 0) {
760 request_error(state);
761 return;
764 user_list = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users);
765 if (!user_list) {
766 request_error(state);
767 return;
769 /* will be freed by process_request() */
770 state->response.extra_data.data = user_list;
772 memset(user_list, 0, num_users * sizeof(struct winbindd_pw));
774 if (!state->getpwent_initialized)
775 winbindd_setpwent_internal(state);
777 if (!(ent = state->getpwent_state)) {
778 request_error(state);
779 return;
782 /* Start sending back users */
784 for (user_list_ndx = 0; user_list_ndx < num_users; ) {
785 struct getpwent_user *name_list = NULL;
786 uint32 result;
788 /* Do we need to fetch another chunk of users? */
790 if (ent->num_sam_entries == ent->sam_entry_index) {
792 while(ent &&
793 !get_sam_user_entries(ent, state->mem_ctx)) {
794 struct getent_state *next_ent;
796 /* Free state information for this domain */
798 SAFE_FREE(ent->sam_entries);
800 next_ent = ent->next;
801 DLIST_REMOVE(state->getpwent_state, ent);
803 SAFE_FREE(ent);
804 ent = next_ent;
807 /* No more domains */
809 if (!ent)
810 break;
813 name_list = (struct getpwent_user *)ent->sam_entries;
815 /* Lookup user info */
817 result = winbindd_fill_pwent(
818 state->mem_ctx,
819 ent->domain_name,
820 name_list[ent->sam_entry_index].name,
821 &name_list[ent->sam_entry_index].user_sid,
822 &name_list[ent->sam_entry_index].group_sid,
823 name_list[ent->sam_entry_index].gecos,
824 name_list[ent->sam_entry_index].homedir,
825 name_list[ent->sam_entry_index].shell,
826 &user_list[user_list_ndx]);
828 /* Add user to return list */
830 if (result) {
832 user_list_ndx++;
833 state->response.data.num_entries++;
834 state->response.length += sizeof(struct winbindd_pw);
836 } else
837 DEBUG(1, ("could not lookup domain user %s\n",
838 name_list[ent->sam_entry_index].name));
840 ent->sam_entry_index++;
844 /* Out of domains */
846 if (user_list_ndx > 0)
847 request_ok(state);
848 else
849 request_error(state);
852 /* List domain users without mapping to unix ids */
853 void winbindd_list_users(struct winbindd_cli_state *state)
855 winbindd_list_ent(state, LIST_USERS);