r22710: Support one-way trusts.
[Samba.git] / source / nsswitch / winbindd_user.c
blob74dc291e0186ecdb7edea7635dbdaa57e9a30319
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 - use template config parameters. The
109 defaults are /tmp for the home directory and /bin/false for
110 shell. */
112 if (!fillup_pw_field(lp_template_homedir(), user_name, dom_name,
113 pw->pw_uid, pw->pw_gid, homedir, pw->pw_dir))
114 return False;
116 if (!fillup_pw_field(lp_template_shell(), user_name, dom_name,
117 pw->pw_uid, pw->pw_gid, shell, pw->pw_shell))
118 return False;
120 /* Password - set to "*" as we can't generate anything useful here.
121 Authentication can be done using the pam_winbind module. */
123 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
125 return True;
128 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
130 enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain,
131 struct winbindd_cli_state *state)
133 DOM_SID sid;
134 WINBIND_USERINFO user_info;
135 NTSTATUS status;
137 /* Ensure null termination */
138 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
140 DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid,
141 state->request.data.sid));
143 if (!string_to_sid(&sid, state->request.data.sid)) {
144 DEBUG(5, ("%s not a SID\n", state->request.data.sid));
145 return WINBINDD_ERROR;
148 status = domain->methods->query_user(domain, state->mem_ctx,
149 &sid, &user_info);
150 if (!NT_STATUS_IS_OK(status)) {
151 DEBUG(1, ("error getting user info for sid %s\n",
152 sid_string_static(&sid)));
153 return WINBINDD_ERROR;
156 fstrcpy(state->response.data.user_info.acct_name, user_info.acct_name);
157 fstrcpy(state->response.data.user_info.full_name, user_info.full_name);
158 fstrcpy(state->response.data.user_info.homedir, user_info.homedir);
159 fstrcpy(state->response.data.user_info.shell, user_info.shell);
160 state->response.data.user_info.primary_gid = user_info.primary_gid;
161 if (!sid_peek_check_rid(&domain->sid, &user_info.group_sid,
162 &state->response.data.user_info.group_rid)) {
163 DEBUG(1, ("Could not extract group rid out of %s\n",
164 sid_string_static(&sid)));
165 return WINBINDD_ERROR;
168 return WINBINDD_OK;
171 struct getpwsid_state {
172 struct winbindd_cli_state *state;
173 struct winbindd_domain *domain;
174 char *username;
175 char *fullname;
176 char *homedir;
177 char *shell;
178 DOM_SID user_sid;
179 uid_t uid;
180 DOM_SID group_sid;
181 gid_t gid;
184 static void getpwsid_queryuser_recv(void *private_data, BOOL success,
185 const char *acct_name,
186 const char *full_name,
187 const char *homedir,
188 const char *shell,
189 uint32 gid,
190 uint32 group_rid);
191 static void getpwsid_sid2uid_recv(void *private_data, BOOL success, uid_t uid);
192 static void getpwsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid);
194 static void winbindd_getpwsid(struct winbindd_cli_state *state,
195 const DOM_SID *sid)
197 struct getpwsid_state *s;
199 s = TALLOC_ZERO_P(state->mem_ctx, struct getpwsid_state);
200 if (s == NULL) {
201 DEBUG(0, ("talloc failed\n"));
202 goto error;
205 s->state = state;
206 s->domain = find_domain_from_sid_noinit(sid);
207 if (s->domain == NULL) {
208 DEBUG(3, ("Could not find domain for sid %s\n",
209 sid_string_static(sid)));
210 goto error;
213 sid_copy(&s->user_sid, sid);
215 query_user_async(s->state->mem_ctx, s->domain, sid,
216 getpwsid_queryuser_recv, s);
217 return;
219 error:
220 request_error(state);
223 static void getpwsid_queryuser_recv(void *private_data, BOOL success,
224 const char *acct_name,
225 const char *full_name,
226 const char *homedir,
227 const char *shell,
228 uint32 gid,
229 uint32 group_rid)
231 fstring username;
232 struct getpwsid_state *s =
233 talloc_get_type_abort(private_data, struct getpwsid_state);
235 if (!success) {
236 DEBUG(5, ("Could not query domain %s SID %s\n", s->domain->name,
237 sid_string_static(&s->user_sid)));
238 request_error(s->state);
239 return;
242 if ( acct_name && *acct_name ) {
243 fstrcpy( username, acct_name );
244 } else {
245 char *domain_name = NULL;
246 enum lsa_SidType type;
247 char *user_name = NULL;
248 struct winbindd_domain *domain = NULL;
250 domain = find_lookup_domain_from_sid(&s->user_sid);
251 winbindd_lookup_name_by_sid(s->state->mem_ctx, domain,
252 &s->user_sid, &domain_name,
253 &user_name, &type );
255 /* If this still fails we ar4e done. Just error out */
256 if ( !user_name ) {
257 DEBUG(5,("Could not obtain a name for SID %s\n",
258 sid_string_static(&s->user_sid)));
259 request_error(s->state);
260 return;
263 fstrcpy( username, user_name );
266 strlower_m( username );
267 s->username = talloc_strdup(s->state->mem_ctx, username);
269 ws_name_replace( s->username, WB_REPLACE_CHAR );
271 s->fullname = talloc_strdup(s->state->mem_ctx, full_name);
272 s->homedir = talloc_strdup(s->state->mem_ctx, homedir);
273 s->shell = talloc_strdup(s->state->mem_ctx, shell);
274 s->gid = gid;
275 sid_copy(&s->group_sid, &s->domain->sid);
276 sid_append_rid(&s->group_sid, group_rid);
278 winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid,
279 getpwsid_sid2uid_recv, s);
282 static void getpwsid_sid2uid_recv(void *private_data, BOOL success, uid_t uid)
284 struct getpwsid_state *s =
285 talloc_get_type_abort(private_data, struct getpwsid_state);
287 if (!success) {
288 DEBUG(5, ("Could not query uid for user %s\\%s\n",
289 s->domain->name, s->username));
290 request_error(s->state);
291 return;
294 s->uid = uid;
295 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
296 getpwsid_sid2gid_recv, s);
299 static void getpwsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
301 struct getpwsid_state *s =
302 talloc_get_type_abort(private_data, struct getpwsid_state);
303 struct winbindd_pw *pw;
304 fstring output_username;
306 /* allow the nss backend to override the primary group ID.
307 If the gid has already been set, then keep it.
308 This makes me feel dirty. If the nss backend already
309 gave us a gid, we don't really care whether the sid2gid()
310 call worked or not. --jerry */
312 if ( s->gid == (gid_t)-1 ) {
314 if (!success) {
315 DEBUG(5, ("Could not query gid for user %s\\%s\n",
316 s->domain->name, s->username));
317 goto failed;
320 /* take what the sid2gid() call gave us */
321 s->gid = gid;
324 pw = &s->state->response.data.pw;
325 pw->pw_uid = s->uid;
326 pw->pw_gid = s->gid;
327 fill_domain_username(output_username, s->domain->name, s->username, True);
328 safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
329 safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1);
331 if (!fillup_pw_field(lp_template_homedir(), s->username, s->domain->name,
332 pw->pw_uid, pw->pw_gid, s->homedir, pw->pw_dir)) {
333 DEBUG(5, ("Could not compose homedir\n"));
334 goto failed;
337 if (!fillup_pw_field(lp_template_shell(), s->username, s->domain->name,
338 pw->pw_uid, pw->pw_gid, s->shell, pw->pw_shell)) {
339 DEBUG(5, ("Could not compose shell\n"));
340 goto failed;
343 /* Password - set to "*" as we can't generate anything useful here.
344 Authentication can be done using the pam_winbind module. */
346 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
348 request_ok(s->state);
349 return;
351 failed:
352 request_error(s->state);
355 /* Return a password structure from a username. */
357 static void getpwnam_name2sid_recv(void *private_data, BOOL success,
358 const DOM_SID *sid, enum lsa_SidType type);
360 void winbindd_getpwnam(struct winbindd_cli_state *state)
362 struct winbindd_domain *domain;
363 fstring domname, username;
365 /* Ensure null termination */
366 state->request.data.username[sizeof(state->request.data.username)-1]='\0';
368 DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state->pid,
369 state->request.data.username));
371 ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
373 if (!parse_domain_user(state->request.data.username, domname,
374 username)) {
375 DEBUG(5, ("Could not parse domain user: %s\n",
376 state->request.data.username));
377 request_error(state);
378 return;
381 /* Get info for the domain */
383 domain = find_domain_from_name(domname);
385 if (domain == NULL) {
386 DEBUG(7, ("could not find domain entry for domain %s. "
387 "Using primary domain\n", domname));
388 if ( (domain = find_our_domain()) == NULL ) {
389 DEBUG(0,("Cannot find my primary domain structure!\n"));
390 request_error(state);
391 return;
395 if ( strequal(domname, lp_workgroup()) && lp_winbind_trusted_domains_only() ) {
396 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getpwnam() for %s\\%s.\n",
397 domname, username));
398 request_error(state);
399 return;
402 /* Get rid and name type from name. The following costs 1 packet */
404 winbindd_lookupname_async(state->mem_ctx, domname, username,
405 getpwnam_name2sid_recv, state);
408 static void getpwnam_name2sid_recv(void *private_data, BOOL success,
409 const DOM_SID *sid, enum lsa_SidType type)
411 struct winbindd_cli_state *state =
412 (struct winbindd_cli_state *)private_data;
413 fstring domname, username;
415 if (!success) {
416 DEBUG(5, ("Could not lookup name for user %s\n",
417 state->request.data.username));
418 request_error(state);
419 return;
422 if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) {
423 DEBUG(5, ("%s is not a user\n", state->request.data.username));
424 request_error(state);
425 return;
428 if ( parse_domain_user(state->request.data.username, domname, username) ) {
429 check_domain_trusted( domname, sid );
434 winbindd_getpwsid(state, sid);
437 static void getpwuid_recv(void *private_data, BOOL success, const char *sid)
439 struct winbindd_cli_state *state =
440 (struct winbindd_cli_state *)private_data;
441 DOM_SID user_sid;
443 if (!success) {
444 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
445 (unsigned long)(state->request.data.uid)));
446 request_error(state);
447 return;
450 DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
451 (unsigned long)(state->request.data.uid), sid));
453 string_to_sid(&user_sid, sid);
454 winbindd_getpwsid(state, &user_sid);
457 /* Return a password structure given a uid number */
458 void winbindd_getpwuid(struct winbindd_cli_state *state)
460 DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state->pid,
461 (unsigned long)state->request.data.uid));
463 /* always query idmap via the async interface */
464 /* if this turns to be too slow we will add here a direct query to the cache */
465 winbindd_uid2sid_async(state->mem_ctx, state->request.data.uid, getpwuid_recv, state);
469 * set/get/endpwent functions
472 /* Rewind file pointer for ntdom passwd database */
474 static BOOL winbindd_setpwent_internal(struct winbindd_cli_state *state)
476 struct winbindd_domain *domain;
478 DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
480 /* Check user has enabled this */
482 if (!lp_winbind_enum_users()) {
483 return False;
486 /* Free old static data if it exists */
488 if (state->getpwent_state != NULL) {
489 free_getent_state(state->getpwent_state);
490 state->getpwent_state = NULL;
493 #if 0 /* JERRY */
494 /* add any local users we have */
496 if ( (domain_state = (struct getent_state *)malloc(sizeof(struct getent_state))) == NULL )
497 return False;
499 ZERO_STRUCTP(domain_state);
501 /* Add to list of open domains */
503 DLIST_ADD(state->getpwent_state, domain_state);
504 #endif
506 /* Create sam pipes for each domain we know about */
508 for(domain = domain_list(); domain != NULL; domain = domain->next) {
509 struct getent_state *domain_state;
512 /* don't add our domaina if we are a PDC or if we
513 are a member of a Samba domain */
515 if ( (IS_DC || lp_winbind_trusted_domains_only())
516 && strequal(domain->name, lp_workgroup()) )
518 continue;
521 /* Create a state record for this domain */
523 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
524 DEBUG(0, ("malloc failed\n"));
525 return False;
528 ZERO_STRUCTP(domain_state);
530 fstrcpy(domain_state->domain_name, domain->name);
532 /* Add to list of open domains */
534 DLIST_ADD(state->getpwent_state, domain_state);
537 state->getpwent_initialized = True;
538 return True;
541 void winbindd_setpwent(struct winbindd_cli_state *state)
543 if (winbindd_setpwent_internal(state)) {
544 request_ok(state);
545 } else {
546 request_error(state);
550 /* Close file pointer to ntdom passwd database */
552 void winbindd_endpwent(struct winbindd_cli_state *state)
554 DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
556 free_getent_state(state->getpwent_state);
557 state->getpwent_initialized = False;
558 state->getpwent_state = NULL;
559 request_ok(state);
562 /* Get partial list of domain users for a domain. We fill in the sam_entries,
563 and num_sam_entries fields with domain user information. The dispinfo_ndx
564 field is incremented to the index of the next user to fetch. Return True if
565 some users were returned, False otherwise. */
567 static BOOL get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
569 NTSTATUS status;
570 uint32 num_entries;
571 WINBIND_USERINFO *info;
572 struct getpwent_user *name_list = NULL;
573 struct winbindd_domain *domain;
574 struct winbindd_methods *methods;
575 unsigned int i;
577 if (ent->num_sam_entries)
578 return False;
580 if (!(domain = find_domain_from_name(ent->domain_name))) {
581 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
582 ent->domain_name));
583 return False;
586 methods = domain->methods;
588 /* Free any existing user info */
590 SAFE_FREE(ent->sam_entries);
591 ent->num_sam_entries = 0;
593 /* Call query_user_list to get a list of usernames and user rids */
595 num_entries = 0;
597 status = methods->query_user_list(domain, mem_ctx, &num_entries,
598 &info);
600 if (!NT_STATUS_IS_OK(status)) {
601 DEBUG(10,("get_sam_user_entries: query_user_list failed with %s\n",
602 nt_errstr(status) ));
603 return False;
606 if (num_entries) {
607 name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user, ent->num_sam_entries + num_entries);
609 if (!name_list) {
610 DEBUG(0,("get_sam_user_entries realloc failed.\n"));
611 return False;
615 for (i = 0; i < num_entries; i++) {
616 /* Store account name and gecos */
617 if (!info[i].acct_name) {
618 fstrcpy(name_list[ent->num_sam_entries + i].name, "");
619 } else {
620 fstrcpy(name_list[ent->num_sam_entries + i].name,
621 info[i].acct_name);
623 if (!info[i].full_name) {
624 fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
625 } else {
626 fstrcpy(name_list[ent->num_sam_entries + i].gecos,
627 info[i].full_name);
629 if (!info[i].homedir) {
630 fstrcpy(name_list[ent->num_sam_entries + i].homedir, "");
631 } else {
632 fstrcpy(name_list[ent->num_sam_entries + i].homedir,
633 info[i].homedir);
635 if (!info[i].shell) {
636 fstrcpy(name_list[ent->num_sam_entries + i].shell, "");
637 } else {
638 fstrcpy(name_list[ent->num_sam_entries + i].shell,
639 info[i].shell);
643 /* User and group ids */
644 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
645 &info[i].user_sid);
646 sid_copy(&name_list[ent->num_sam_entries+i].group_sid,
647 &info[i].group_sid);
650 ent->num_sam_entries += num_entries;
652 /* Fill in remaining fields */
654 ent->sam_entries = name_list;
655 ent->sam_entry_index = 0;
656 return ent->num_sam_entries > 0;
659 /* Fetch next passwd entry from ntdom database */
661 #define MAX_GETPWENT_USERS 500
663 void winbindd_getpwent(struct winbindd_cli_state *state)
665 struct getent_state *ent;
666 struct winbindd_pw *user_list;
667 int num_users, user_list_ndx = 0, i;
669 DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
671 /* Check user has enabled this */
673 if (!lp_winbind_enum_users()) {
674 request_error(state);
675 return;
678 /* Allocate space for returning a chunk of users */
680 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
682 if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users)) == NULL) {
683 request_error(state);
684 return;
687 memset(state->response.extra_data.data, 0, num_users *
688 sizeof(struct winbindd_pw));
690 user_list = (struct winbindd_pw *)state->response.extra_data.data;
692 if (!state->getpwent_initialized)
693 winbindd_setpwent_internal(state);
695 if (!(ent = state->getpwent_state)) {
696 request_error(state);
697 return;
700 /* Start sending back users */
702 for (i = 0; i < num_users; i++) {
703 struct getpwent_user *name_list = NULL;
704 uint32 result;
706 /* Do we need to fetch another chunk of users? */
708 if (ent->num_sam_entries == ent->sam_entry_index) {
710 while(ent &&
711 !get_sam_user_entries(ent, state->mem_ctx)) {
712 struct getent_state *next_ent;
714 /* Free state information for this domain */
716 SAFE_FREE(ent->sam_entries);
718 next_ent = ent->next;
719 DLIST_REMOVE(state->getpwent_state, ent);
721 SAFE_FREE(ent);
722 ent = next_ent;
725 /* No more domains */
727 if (!ent)
728 break;
731 name_list = (struct getpwent_user *)ent->sam_entries;
733 /* Lookup user info */
735 result = winbindd_fill_pwent(
736 ent->domain_name,
737 name_list[ent->sam_entry_index].name,
738 &name_list[ent->sam_entry_index].user_sid,
739 &name_list[ent->sam_entry_index].group_sid,
740 name_list[ent->sam_entry_index].gecos,
741 name_list[ent->sam_entry_index].homedir,
742 name_list[ent->sam_entry_index].shell,
743 &user_list[user_list_ndx]);
745 ent->sam_entry_index++;
747 /* Add user to return list */
749 if (result) {
751 user_list_ndx++;
752 state->response.data.num_entries++;
753 state->response.length +=
754 sizeof(struct winbindd_pw);
756 } else
757 DEBUG(1, ("could not lookup domain user %s\n",
758 name_list[ent->sam_entry_index].name));
761 /* Out of domains */
763 if (user_list_ndx > 0)
764 request_ok(state);
765 else
766 request_error(state);
769 /* List domain users without mapping to unix ids */
771 void winbindd_list_users(struct winbindd_cli_state *state)
773 struct winbindd_domain *domain;
774 WINBIND_USERINFO *info;
775 const char *which_domain;
776 uint32 num_entries = 0, total_entries = 0;
777 char *extra_data = NULL;
778 int extra_data_len = 0;
779 enum winbindd_result rv = WINBINDD_ERROR;
781 DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid));
783 /* Ensure null termination */
784 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
785 which_domain = state->request.domain_name;
787 /* Enumerate over trusted domains */
789 for (domain = domain_list(); domain; domain = domain->next) {
790 NTSTATUS status;
791 struct winbindd_methods *methods;
792 unsigned int i;
794 /* if we have a domain name restricting the request and this
795 one in the list doesn't match, then just bypass the remainder
796 of the loop */
798 if ( *which_domain && !strequal(which_domain, domain->name) )
799 continue;
801 methods = domain->methods;
803 /* Query display info */
804 status = methods->query_user_list(domain, state->mem_ctx,
805 &num_entries, &info);
807 if (!NT_STATUS_IS_OK(status)) {
808 continue;
811 if (num_entries == 0)
812 continue;
814 /* Allocate some memory for extra data */
815 total_entries += num_entries;
817 extra_data = (char *)SMB_REALLOC(
818 extra_data, sizeof(fstring) * total_entries);
820 if (!extra_data) {
821 DEBUG(0,("failed to enlarge buffer!\n"));
822 goto done;
825 /* Pack user list into extra data fields */
827 for (i = 0; i < num_entries; i++) {
828 fstring acct_name, name;
830 if (!info[i].acct_name) {
831 fstrcpy(acct_name, "");
832 } else {
833 fstrcpy(acct_name, info[i].acct_name);
836 fill_domain_username(name, domain->name, acct_name, True);
838 /* Append to extra data */
839 memcpy(&extra_data[extra_data_len], name,
840 strlen(name));
841 extra_data_len += strlen(name);
842 extra_data[extra_data_len++] = ',';
846 /* Assign extra_data fields in response structure */
848 if (extra_data) {
849 extra_data[extra_data_len - 1] = '\0';
850 state->response.extra_data.data = extra_data;
851 state->response.length += extra_data_len;
854 /* No domains responded but that's still OK so don't return an
855 error. */
857 rv = WINBINDD_OK;
859 done:
861 if (rv == WINBINDD_OK)
862 request_ok(state);
863 else
864 request_error(state);