WHATSNEW: Update changes since 3.3.0pre2.
[Samba.git] / source / winbindd / winbindd_user.c
blobe5d0a22a7324e03c905d1832fa526de3121f8f56
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;
80 if (!pw || !dom_name || !user_name)
81 return False;
83 /* Resolve the uid number */
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;
92 /* Resolve the gid number */
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;
101 strlower_m(user_name);
103 /* Username */
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;
115 /* Basic removal of whitespace */
116 if (NT_STATUS_IS_OK(nt_status)) {
117 fill_domain_username(output_username, dom_name, mapped_name, True);
119 /* Complete name replacement */
120 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
121 fstrcpy(output_username, mapped_name);
123 /* No change at all */
124 else {
125 fill_domain_username(output_username, dom_name, user_name, True);
128 safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
130 /* Full name (gecos) */
132 safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
134 /* Home directory and shell */
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;
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;
144 /* Password - set to "*" as we can't generate anything useful here.
145 Authentication can be done using the pam_winbind module. */
147 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
149 return True;
152 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
154 enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain,
155 struct winbindd_cli_state *state)
157 DOM_SID sid;
158 WINBIND_USERINFO user_info;
159 NTSTATUS status;
161 /* Ensure null termination */
162 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
164 DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid,
165 state->request.data.sid));
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;
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;
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;
192 return WINBINDD_OK;
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;
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);
219 static void winbindd_getpwsid(struct winbindd_cli_state *state,
220 const DOM_SID *sid)
222 struct getpwsid_state *s;
224 s = TALLOC_ZERO_P(state->mem_ctx, struct getpwsid_state);
225 if (s == NULL) {
226 DEBUG(0, ("talloc failed\n"));
227 goto error;
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;
238 sid_copy(&s->user_sid, sid);
240 query_user_async(s->state->mem_ctx, s->domain, sid,
241 getpwsid_queryuser_recv, s);
242 return;
244 error:
245 request_error(state);
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)
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;
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;
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;
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;
284 winbindd_lookup_name_by_sid(s->state->mem_ctx, domain,
285 &s->user_sid, &domain_name,
286 &user_name, &type );
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;
296 fstrcpy( username, user_name );
299 strlower_m( username );
300 s->username = talloc_strdup(s->state->mem_ctx, username);
302 nt_status = normalize_name_map(s->state->mem_ctx, s->domain,
303 s->username, &mapped_name);
305 /* Basic removal of whitespace */
306 if (NT_STATUS_IS_OK(nt_status)) {
307 s->username = mapped_name;
308 s->username_mapped = false;
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;
315 /* No change at all */
316 else {
317 s->username_mapped = false;
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);
327 winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid,
328 getpwsid_sid2uid_recv, s);
331 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid)
333 struct getpwsid_state *s =
334 talloc_get_type_abort(private_data, struct getpwsid_state);
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;
343 s->uid = uid;
344 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
345 getpwsid_sid2gid_recv, s);
348 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
350 struct getpwsid_state *s =
351 talloc_get_type_abort(private_data, struct getpwsid_state);
352 struct winbindd_pw *pw;
353 fstring output_username;
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 */
361 if ( s->gid == (gid_t)-1 ) {
363 if (!success) {
364 DEBUG(5, ("Could not query gid for user %s\\%s\n",
365 s->domain->name, s->username));
366 goto failed;
369 /* take what the sid2gid() call gave us */
370 s->gid = gid;
373 pw = &s->state->response.data.pw;
374 pw->pw_uid = s->uid;
375 pw->pw_gid = s->gid;
377 /* allow username to be overridden by the alias mapping */
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);
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);
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;
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;
403 /* Password - set to "*" as we can't generate anything useful here.
404 Authentication can be done using the pam_winbind module. */
406 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
408 request_ok(s->state);
409 return;
411 failed:
412 request_error(s->state);
415 /* Return a password structure from a username. */
417 static void getpwnam_name2sid_recv(void *private_data, bool success,
418 const DOM_SID *sid, enum lsa_SidType type);
420 void winbindd_getpwnam(struct winbindd_cli_state *state)
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;
429 domuser = state->request.data.username;
430 dusize = sizeof(state->request.data.username);
432 /* Ensure null termination (it's an fstring) */
433 domuser[dusize-1] = '\0';
435 DEBUG(3, ("[%5lu]: getpwnam %s\n",
436 (unsigned long)state->pid,
437 domuser));
439 nt_status = normalize_name_unmap(state->mem_ctx, domuser,
440 &mapped_user);
442 /* If we could not convert from an aliased name or a
443 normalized name, then just use the original name */
445 if (!NT_STATUS_IS_OK(nt_status) &&
446 !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
448 mapped_user = domuser;
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;
457 /* Get info for the domain */
459 domain = find_domain_from_name(domname);
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;
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;
480 /* Get rid and name type from name. The following costs 1 packet */
482 winbindd_lookupname_async(state->mem_ctx, domname, username,
483 getpwnam_name2sid_recv, WINBINDD_GETPWNAM,
484 state);
487 static void getpwnam_name2sid_recv(void *private_data, bool success,
488 const DOM_SID *sid, enum lsa_SidType type)
490 struct winbindd_cli_state *state =
491 (struct winbindd_cli_state *)private_data;
492 fstring domname, username;
493 char *domuser = state->request.data.username;
495 if (!success) {
496 DEBUG(5, ("Could not lookup name for user %s\n", domuser));
497 request_error(state);
498 return;
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;
507 if (parse_domain_user(domuser, domname, username)) {
508 check_domain_trusted(domname, sid);
511 winbindd_getpwsid(state, sid);
514 static void getpwuid_recv(void *private_data, bool success, const char *sid)
516 struct winbindd_cli_state *state =
517 (struct winbindd_cli_state *)private_data;
518 DOM_SID user_sid;
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;
527 DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
528 (unsigned long)(state->request.data.uid), sid));
530 string_to_sid(&user_sid, sid);
531 winbindd_getpwsid(state, &user_sid);
534 /* Return a password structure given a uid number */
535 void winbindd_getpwuid(struct winbindd_cli_state *state)
537 uid_t uid = state->request.data.uid;
539 DEBUG(3, ("[%5lu]: getpwuid %lu\n",
540 (unsigned long)state->pid,
541 (unsigned long)uid));
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);
550 * set/get/endpwent functions
553 /* Rewind file pointer for ntdom passwd database */
555 static bool winbindd_setpwent_internal(struct winbindd_cli_state *state)
557 struct winbindd_domain *domain;
559 DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
561 /* Check user has enabled this */
563 if (!lp_winbind_enum_users()) {
564 return False;
567 /* Free old static data if it exists */
569 if (state->getpwent_state != NULL) {
570 free_getent_state(state->getpwent_state);
571 state->getpwent_state = NULL;
574 /* Create sam pipes for each domain we know about */
576 for(domain = domain_list(); domain != NULL; domain = domain->next) {
577 struct getent_state *domain_state;
580 /* don't add our domaina if we are a PDC or if we
581 are a member of a Samba domain */
583 if ((IS_DC || lp_winbind_trusted_domains_only())
584 && strequal(domain->name, lp_workgroup())) {
585 continue;
588 /* Create a state record for this domain */
590 domain_state = SMB_MALLOC_P(struct getent_state);
591 if (!domain_state) {
592 DEBUG(0, ("malloc failed\n"));
593 return False;
596 ZERO_STRUCTP(domain_state);
598 fstrcpy(domain_state->domain_name, domain->name);
600 /* Add to list of open domains */
602 DLIST_ADD(state->getpwent_state, domain_state);
605 state->getpwent_initialized = True;
606 return True;
609 void winbindd_setpwent(struct winbindd_cli_state *state)
611 if (winbindd_setpwent_internal(state)) {
612 request_ok(state);
613 } else {
614 request_error(state);
618 /* Close file pointer to ntdom passwd database */
620 void winbindd_endpwent(struct winbindd_cli_state *state)
622 DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
624 free_getent_state(state->getpwent_state);
625 state->getpwent_initialized = False;
626 state->getpwent_state = NULL;
627 request_ok(state);
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. */
635 static bool get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
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;
645 if (ent->num_sam_entries)
646 return False;
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;
654 methods = domain->methods;
656 /* Free any existing user info */
658 SAFE_FREE(ent->sam_entries);
659 ent->num_sam_entries = 0;
661 /* Call query_user_list to get a list of usernames and user rids */
663 num_entries = 0;
665 status = methods->query_user_list(domain, mem_ctx, &num_entries, &info);
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;
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;
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);
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);
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);
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);
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);
718 ent->num_sam_entries += num_entries;
720 /* Fill in remaining fields */
722 ent->sam_entries = name_list;
723 ent->sam_entry_index = 0;
724 return ent->num_sam_entries > 0;
727 /* Fetch next passwd entry from ntdom database */
729 #define MAX_GETPWENT_USERS 500
731 void winbindd_getpwent(struct winbindd_cli_state *state)
733 struct getent_state *ent;
734 struct winbindd_pw *user_list;
735 int num_users, user_list_ndx;
737 DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
739 /* Check user has enabled this */
741 if (!lp_winbind_enum_users()) {
742 request_error(state);
743 return;
746 /* Allocate space for returning a chunk of users */
748 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
750 if (num_users == 0) {
751 request_error(state);
752 return;
755 user_list = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users);
756 if (!user_list) {
757 request_error(state);
758 return;
760 /* will be freed by process_request() */
761 state->response.extra_data.data = user_list;
763 memset(user_list, 0, num_users * sizeof(struct winbindd_pw));
765 if (!state->getpwent_initialized)
766 winbindd_setpwent_internal(state);
768 if (!(ent = state->getpwent_state)) {
769 request_error(state);
770 return;
773 /* Start sending back users */
775 for (user_list_ndx = 0; user_list_ndx < num_users; ) {
776 struct getpwent_user *name_list = NULL;
777 uint32 result;
779 /* Do we need to fetch another chunk of users? */
781 if (ent->num_sam_entries == ent->sam_entry_index) {
783 while(ent &&
784 !get_sam_user_entries(ent, state->mem_ctx)) {
785 struct getent_state *next_ent;
787 /* Free state information for this domain */
789 SAFE_FREE(ent->sam_entries);
791 next_ent = ent->next;
792 DLIST_REMOVE(state->getpwent_state, ent);
794 SAFE_FREE(ent);
795 ent = next_ent;
798 /* No more domains */
800 if (!ent)
801 break;
804 name_list = (struct getpwent_user *)ent->sam_entries;
806 /* Lookup user info */
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]);
819 /* Add user to return list */
821 if (result) {
823 user_list_ndx++;
824 state->response.data.num_entries++;
825 state->response.length += sizeof(struct winbindd_pw);
827 } else
828 DEBUG(1, ("could not lookup domain user %s\n",
829 name_list[ent->sam_entry_index].name));
831 ent->sam_entry_index++;
835 /* Out of domains */
837 if (user_list_ndx > 0)
838 request_ok(state);
839 else
840 request_error(state);
843 /* List domain users without mapping to unix ids */
844 void winbindd_list_users(struct winbindd_cli_state *state)
846 winbindd_list_ent(state, LIST_USERS);