r22812: Fix bug #3024 (and also the group varient). Patch from
[Samba/bb.git] / source3 / nsswitch / winbindd_user.c
blob48b470cdd0dae54f012e23ed95d69e38062adee4
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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "includes.h"
26 #include "winbindd.h"
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_WINBIND
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)
39 char *templ;
41 if (out == NULL)
42 return False;
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. */
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);
60 if (!templ)
61 return False;
63 safe_strcpy(out, templ, sizeof(fstring) - 1);
64 TALLOC_FREE(templ);
66 return True;
69 /* Fill a pwent structure with information we have obtained */
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)
76 fstring output_username;
77 fstring sid_string;
79 if (!pw || !dom_name || !user_name)
80 return False;
82 /* Resolve the uid number */
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;
89 /* Resolve the gid number */
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;
96 strlower_m(user_name);
98 /* Username */
100 fill_domain_username(output_username, dom_name, user_name, True);
102 safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
104 /* Full name (gecos) */
106 safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
108 /* Home directory and shell */
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;
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;
118 /* Password - set to "*" as we can't generate anything useful here.
119 Authentication can be done using the pam_winbind module. */
121 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
123 return True;
126 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
128 enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain,
129 struct winbindd_cli_state *state)
131 DOM_SID sid;
132 WINBIND_USERINFO user_info;
133 NTSTATUS status;
135 /* Ensure null termination */
136 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
138 DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid,
139 state->request.data.sid));
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;
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;
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;
166 return WINBINDD_OK;
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;
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);
192 static void winbindd_getpwsid(struct winbindd_cli_state *state,
193 const DOM_SID *sid)
195 struct getpwsid_state *s;
197 s = TALLOC_ZERO_P(state->mem_ctx, struct getpwsid_state);
198 if (s == NULL) {
199 DEBUG(0, ("talloc failed\n"));
200 goto error;
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;
211 sid_copy(&s->user_sid, sid);
213 query_user_async(s->state->mem_ctx, s->domain, sid,
214 getpwsid_queryuser_recv, s);
215 return;
217 error:
218 request_error(state);
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)
229 fstring username;
230 struct getpwsid_state *s =
231 talloc_get_type_abort(private_data, struct getpwsid_state);
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;
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;
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 );
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;
261 fstrcpy( username, user_name );
264 strlower_m( username );
265 s->username = talloc_strdup(s->state->mem_ctx, username);
267 ws_name_replace( s->username, WB_REPLACE_CHAR );
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);
276 winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid,
277 getpwsid_sid2uid_recv, s);
280 static void getpwsid_sid2uid_recv(void *private_data, BOOL success, uid_t uid)
282 struct getpwsid_state *s =
283 talloc_get_type_abort(private_data, struct getpwsid_state);
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;
292 s->uid = uid;
293 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
294 getpwsid_sid2gid_recv, s);
297 static void getpwsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
299 struct getpwsid_state *s =
300 talloc_get_type_abort(private_data, struct getpwsid_state);
301 struct winbindd_pw *pw;
302 fstring output_username;
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 */
310 if ( s->gid == (gid_t)-1 ) {
312 if (!success) {
313 DEBUG(5, ("Could not query gid for user %s\\%s\n",
314 s->domain->name, s->username));
315 goto failed;
318 /* take what the sid2gid() call gave us */
319 s->gid = gid;
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);
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;
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;
341 /* Password - set to "*" as we can't generate anything useful here.
342 Authentication can be done using the pam_winbind module. */
344 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
346 request_ok(s->state);
347 return;
349 failed:
350 request_error(s->state);
353 /* Return a password structure from a username. */
355 static void getpwnam_name2sid_recv(void *private_data, BOOL success,
356 const DOM_SID *sid, enum lsa_SidType type);
358 void winbindd_getpwnam(struct winbindd_cli_state *state)
360 struct winbindd_domain *domain;
361 fstring domname, username;
363 /* Ensure null termination */
364 state->request.data.username[sizeof(state->request.data.username)-1]='\0';
366 DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state->pid,
367 state->request.data.username));
369 ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
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;
379 /* Get info for the domain */
381 domain = find_domain_from_name(domname);
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;
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;
400 /* Get rid and name type from name. The following costs 1 packet */
402 winbindd_lookupname_async(state->mem_ctx, domname, username,
403 getpwnam_name2sid_recv, state);
406 static void getpwnam_name2sid_recv(void *private_data, BOOL success,
407 const DOM_SID *sid, enum lsa_SidType type)
409 struct winbindd_cli_state *state =
410 (struct winbindd_cli_state *)private_data;
411 fstring domname, username;
413 if (!success) {
414 DEBUG(5, ("Could not lookup name for user %s\n",
415 state->request.data.username));
416 request_error(state);
417 return;
420 if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) {
421 DEBUG(5, ("%s is not a user\n", state->request.data.username));
422 request_error(state);
423 return;
426 if ( parse_domain_user(state->request.data.username, domname, username) ) {
427 check_domain_trusted( domname, sid );
432 winbindd_getpwsid(state, sid);
435 static void getpwuid_recv(void *private_data, BOOL success, const char *sid)
437 struct winbindd_cli_state *state =
438 (struct winbindd_cli_state *)private_data;
439 DOM_SID user_sid;
441 if (!success) {
442 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
443 (unsigned long)(state->request.data.uid)));
444 request_error(state);
445 return;
448 DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
449 (unsigned long)(state->request.data.uid), sid));
451 string_to_sid(&user_sid, sid);
452 winbindd_getpwsid(state, &user_sid);
455 /* Return a password structure given a uid number */
456 void winbindd_getpwuid(struct winbindd_cli_state *state)
458 DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state->pid,
459 (unsigned long)state->request.data.uid));
461 /* always query idmap via the async interface */
462 /* if this turns to be too slow we will add here a direct query to the cache */
463 winbindd_uid2sid_async(state->mem_ctx, state->request.data.uid, getpwuid_recv, state);
467 * set/get/endpwent functions
470 /* Rewind file pointer for ntdom passwd database */
472 static BOOL winbindd_setpwent_internal(struct winbindd_cli_state *state)
474 struct winbindd_domain *domain;
476 DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
478 /* Check user has enabled this */
480 if (!lp_winbind_enum_users()) {
481 return False;
484 /* Free old static data if it exists */
486 if (state->getpwent_state != NULL) {
487 free_getent_state(state->getpwent_state);
488 state->getpwent_state = NULL;
491 #if 0 /* JERRY */
492 /* add any local users we have */
494 if ( (domain_state = (struct getent_state *)malloc(sizeof(struct getent_state))) == NULL )
495 return False;
497 ZERO_STRUCTP(domain_state);
499 /* Add to list of open domains */
501 DLIST_ADD(state->getpwent_state, domain_state);
502 #endif
504 /* Create sam pipes for each domain we know about */
506 for(domain = domain_list(); domain != NULL; domain = domain->next) {
507 struct getent_state *domain_state;
510 /* don't add our domaina if we are a PDC or if we
511 are a member of a Samba domain */
513 if ( (IS_DC || lp_winbind_trusted_domains_only())
514 && strequal(domain->name, lp_workgroup()) )
516 continue;
519 /* Create a state record for this domain */
521 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
522 DEBUG(0, ("malloc failed\n"));
523 return False;
526 ZERO_STRUCTP(domain_state);
528 fstrcpy(domain_state->domain_name, domain->name);
530 /* Add to list of open domains */
532 DLIST_ADD(state->getpwent_state, domain_state);
535 state->getpwent_initialized = True;
536 return True;
539 void winbindd_setpwent(struct winbindd_cli_state *state)
541 if (winbindd_setpwent_internal(state)) {
542 request_ok(state);
543 } else {
544 request_error(state);
548 /* Close file pointer to ntdom passwd database */
550 void winbindd_endpwent(struct winbindd_cli_state *state)
552 DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
554 free_getent_state(state->getpwent_state);
555 state->getpwent_initialized = False;
556 state->getpwent_state = NULL;
557 request_ok(state);
560 /* Get partial list of domain users for a domain. We fill in the sam_entries,
561 and num_sam_entries fields with domain user information. The dispinfo_ndx
562 field is incremented to the index of the next user to fetch. Return True if
563 some users were returned, False otherwise. */
565 static BOOL get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
567 NTSTATUS status;
568 uint32 num_entries;
569 WINBIND_USERINFO *info;
570 struct getpwent_user *name_list = NULL;
571 struct winbindd_domain *domain;
572 struct winbindd_methods *methods;
573 unsigned int i;
575 if (ent->num_sam_entries)
576 return False;
578 if (!(domain = find_domain_from_name(ent->domain_name))) {
579 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
580 ent->domain_name));
581 return False;
584 methods = domain->methods;
586 /* Free any existing user info */
588 SAFE_FREE(ent->sam_entries);
589 ent->num_sam_entries = 0;
591 /* Call query_user_list to get a list of usernames and user rids */
593 num_entries = 0;
595 status = methods->query_user_list(domain, mem_ctx, &num_entries,
596 &info);
598 if (!NT_STATUS_IS_OK(status)) {
599 DEBUG(10,("get_sam_user_entries: query_user_list failed with %s\n",
600 nt_errstr(status) ));
601 return False;
604 if (num_entries) {
605 name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user, ent->num_sam_entries + num_entries);
607 if (!name_list) {
608 DEBUG(0,("get_sam_user_entries realloc failed.\n"));
609 return False;
613 for (i = 0; i < num_entries; i++) {
614 /* Store account name and gecos */
615 if (!info[i].acct_name) {
616 fstrcpy(name_list[ent->num_sam_entries + i].name, "");
617 } else {
618 fstrcpy(name_list[ent->num_sam_entries + i].name,
619 info[i].acct_name);
621 if (!info[i].full_name) {
622 fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
623 } else {
624 fstrcpy(name_list[ent->num_sam_entries + i].gecos,
625 info[i].full_name);
627 if (!info[i].homedir) {
628 fstrcpy(name_list[ent->num_sam_entries + i].homedir, "");
629 } else {
630 fstrcpy(name_list[ent->num_sam_entries + i].homedir,
631 info[i].homedir);
633 if (!info[i].shell) {
634 fstrcpy(name_list[ent->num_sam_entries + i].shell, "");
635 } else {
636 fstrcpy(name_list[ent->num_sam_entries + i].shell,
637 info[i].shell);
641 /* User and group ids */
642 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
643 &info[i].user_sid);
644 sid_copy(&name_list[ent->num_sam_entries+i].group_sid,
645 &info[i].group_sid);
648 ent->num_sam_entries += num_entries;
650 /* Fill in remaining fields */
652 ent->sam_entries = name_list;
653 ent->sam_entry_index = 0;
654 return ent->num_sam_entries > 0;
657 /* Fetch next passwd entry from ntdom database */
659 #define MAX_GETPWENT_USERS 500
661 void winbindd_getpwent(struct winbindd_cli_state *state)
663 struct getent_state *ent;
664 struct winbindd_pw *user_list;
665 int num_users, user_list_ndx;
667 DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
669 /* Check user has enabled this */
671 if (!lp_winbind_enum_users()) {
672 request_error(state);
673 return;
676 /* Allocate space for returning a chunk of users */
678 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
680 if (num_users == 0) {
681 request_error(state);
682 return;
685 if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users)) == NULL) {
686 request_error(state);
687 return;
690 memset(state->response.extra_data.data, 0, num_users *
691 sizeof(struct winbindd_pw));
693 user_list = (struct winbindd_pw *)state->response.extra_data.data;
695 if (!state->getpwent_initialized)
696 winbindd_setpwent_internal(state);
698 if (!(ent = state->getpwent_state)) {
699 request_error(state);
700 return;
703 /* Start sending back users */
705 for (user_list_ndx = 0; user_list_ndx < num_users; ) {
706 struct getpwent_user *name_list = NULL;
707 uint32 result;
709 /* Do we need to fetch another chunk of users? */
711 if (ent->num_sam_entries == ent->sam_entry_index) {
713 while(ent &&
714 !get_sam_user_entries(ent, state->mem_ctx)) {
715 struct getent_state *next_ent;
717 /* Free state information for this domain */
719 SAFE_FREE(ent->sam_entries);
721 next_ent = ent->next;
722 DLIST_REMOVE(state->getpwent_state, ent);
724 SAFE_FREE(ent);
725 ent = next_ent;
728 /* No more domains */
730 if (!ent)
731 break;
734 name_list = (struct getpwent_user *)ent->sam_entries;
736 /* Lookup user info */
738 result = winbindd_fill_pwent(
739 ent->domain_name,
740 name_list[ent->sam_entry_index].name,
741 &name_list[ent->sam_entry_index].user_sid,
742 &name_list[ent->sam_entry_index].group_sid,
743 name_list[ent->sam_entry_index].gecos,
744 name_list[ent->sam_entry_index].homedir,
745 name_list[ent->sam_entry_index].shell,
746 &user_list[user_list_ndx]);
748 /* Add user to return list */
750 if (result) {
752 user_list_ndx++;
753 state->response.data.num_entries++;
754 state->response.length +=
755 sizeof(struct winbindd_pw);
757 } else
758 DEBUG(1, ("could not lookup domain user %s\n",
759 name_list[ent->sam_entry_index].name));
761 ent->sam_entry_index++;
765 /* Out of domains */
767 if (user_list_ndx > 0)
768 request_ok(state);
769 else
770 request_error(state);
773 /* List domain users without mapping to unix ids */
775 void winbindd_list_users(struct winbindd_cli_state *state)
777 struct winbindd_domain *domain;
778 WINBIND_USERINFO *info;
779 const char *which_domain;
780 uint32 num_entries = 0, total_entries = 0;
781 char *extra_data = NULL;
782 int extra_data_len = 0;
783 enum winbindd_result rv = WINBINDD_ERROR;
785 DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid));
787 /* Ensure null termination */
788 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
789 which_domain = state->request.domain_name;
791 /* Enumerate over trusted domains */
793 for (domain = domain_list(); domain; domain = domain->next) {
794 NTSTATUS status;
795 struct winbindd_methods *methods;
796 unsigned int i;
798 /* if we have a domain name restricting the request and this
799 one in the list doesn't match, then just bypass the remainder
800 of the loop */
802 if ( *which_domain && !strequal(which_domain, domain->name) )
803 continue;
805 methods = domain->methods;
807 /* Query display info */
808 status = methods->query_user_list(domain, state->mem_ctx,
809 &num_entries, &info);
811 if (!NT_STATUS_IS_OK(status)) {
812 continue;
815 if (num_entries == 0)
816 continue;
818 /* Allocate some memory for extra data */
819 total_entries += num_entries;
821 extra_data = (char *)SMB_REALLOC(
822 extra_data, sizeof(fstring) * total_entries);
824 if (!extra_data) {
825 DEBUG(0,("failed to enlarge buffer!\n"));
826 goto done;
829 /* Pack user list into extra data fields */
831 for (i = 0; i < num_entries; i++) {
832 fstring acct_name, name;
834 if (!info[i].acct_name) {
835 fstrcpy(acct_name, "");
836 } else {
837 fstrcpy(acct_name, info[i].acct_name);
840 fill_domain_username(name, domain->name, acct_name, True);
842 /* Append to extra data */
843 memcpy(&extra_data[extra_data_len], name,
844 strlen(name));
845 extra_data_len += strlen(name);
846 extra_data[extra_data_len++] = ',';
850 /* Assign extra_data fields in response structure */
852 if (extra_data) {
853 extra_data[extra_data_len - 1] = '\0';
854 state->response.extra_data.data = extra_data;
855 state->response.length += extra_data_len;
858 /* No domains responded but that's still OK so don't return an
859 error. */
861 rv = WINBINDD_OK;
863 done:
865 if (rv == WINBINDD_OK)
866 request_ok(state);
867 else
868 request_error(state);