r7877: Attempt to fix a smb_panic reported by Pavel Rochnyack.
[tprouty/samba.git] / source / nsswitch / 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 2 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, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_WINBIND
30
31 extern userdom_struct current_user_info;
32
33 /* Fill a pwent structure with information we have obtained */
34
35 static BOOL winbindd_fill_pwent(char *dom_name, char *user_name, 
36                                 DOM_SID *user_sid, DOM_SID *group_sid,
37                                 char *full_name, struct winbindd_pw *pw)
38 {
39         fstring output_username;
40         char *homedir;
41         char *shell;
42         fstring sid_string;
43         
44         if (!pw || !dom_name || !user_name)
45                 return False;
46         
47         /* Resolve the uid number */
48
49         if (!NT_STATUS_IS_OK(idmap_sid_to_uid(user_sid, &pw->pw_uid, 0))) {
50                 DEBUG(1, ("error getting user id for sid %s\n", sid_to_string(sid_string, user_sid)));
51                 return False;
52         }
53         
54         /* Resolve the gid number */   
55
56         if (!NT_STATUS_IS_OK(idmap_sid_to_gid(group_sid, &pw->pw_gid, 0))) {
57                 DEBUG(1, ("error getting group id for sid %s\n", sid_to_string(sid_string, group_sid)));
58                 return False;
59         }
60
61         strlower_m(user_name);
62
63         /* Username */
64
65         fill_domain_username(output_username, dom_name, user_name); 
66
67         safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
68         
69         /* Full name (gecos) */
70         
71         safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
72
73         /* Home directory and shell - use template config parameters.  The
74            defaults are /tmp for the home directory and /bin/false for
75            shell. */
76         
77         /* The substitution of %U and %D in the 'template homedir' is done
78            by alloc_sub_specified() below. */
79
80         fstrcpy(current_user_info.domain, dom_name);
81
82         homedir = alloc_sub_specified(lp_template_homedir(), user_name, dom_name, pw->pw_uid, pw->pw_gid);
83
84         if (!homedir)
85                 return False;
86         
87         safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1);
88         
89         SAFE_FREE(homedir);
90         
91         shell = alloc_sub_specified(lp_template_shell(), user_name, dom_name, pw->pw_uid, pw->pw_gid);
92
93         if (!shell)
94                 return False;
95
96         safe_strcpy(pw->pw_shell, shell, 
97                     sizeof(pw->pw_shell) - 1);
98         
99         SAFE_FREE(shell);
100
101         /* Password - set to "x" as we can't generate anything useful here.
102            Authentication can be done using the pam_winbind module. */
103
104         safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
105
106         return True;
107 }
108
109 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
110
111 enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain,
112                                             struct winbindd_cli_state *state)
113 {
114         DOM_SID sid;
115         WINBIND_USERINFO user_info;
116         NTSTATUS status;
117
118         /* Ensure null termination */
119         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
120
121         DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid, 
122                   state->request.data.sid));
123
124         if (!string_to_sid(&sid, state->request.data.sid)) {
125                 DEBUG(5, ("%s not a SID\n", state->request.data.sid));
126                 return WINBINDD_ERROR;
127         }
128
129         status = domain->methods->query_user(domain, state->mem_ctx,
130                                              &sid, &user_info);
131         if (!NT_STATUS_IS_OK(status)) {
132                 DEBUG(1, ("error getting user info for sid %s\n",
133                           sid_string_static(&sid)));
134                 return WINBINDD_ERROR;
135         }
136
137         fstrcpy(state->response.data.user_info.acct_name, user_info.acct_name);
138         fstrcpy(state->response.data.user_info.full_name, user_info.full_name);
139         if (!sid_peek_check_rid(&domain->sid, &user_info.group_sid,
140                                 &state->response.data.user_info.group_rid)) {
141                 DEBUG(1, ("Could not extract group rid out of %s\n",
142                           sid_string_static(&sid)));
143                 return WINBINDD_ERROR;
144         }
145
146         return WINBINDD_OK;
147 }
148
149 struct getpwsid_state {
150         struct winbindd_cli_state *state;
151         struct winbindd_domain *domain;
152         char *username;
153         char *fullname;
154         DOM_SID user_sid;
155         uid_t uid;
156         DOM_SID group_sid;
157         gid_t gid;
158 };
159
160 static void getpwsid_queryuser_recv(void *private, BOOL success,
161                                     const char *acct_name,
162                                     const char *full_name, uint32 group_rid);
163 static void getpwsid_sid2uid_recv(void *private, BOOL success, uid_t uid);
164 static void getpwsid_sid2gid_recv(void *private, BOOL success, gid_t gid);
165
166 static void winbindd_getpwsid(struct winbindd_cli_state *state,
167                               const DOM_SID *sid)
168 {
169         struct getpwsid_state *s;
170
171         s = TALLOC_P(state->mem_ctx, struct getpwsid_state);
172         if (s == NULL) {
173                 DEBUG(0, ("talloc failed\n"));
174                 goto error;
175         }
176
177         s->state = state;
178         s->domain = find_domain_from_sid_noinit(sid);
179         if (s->domain == NULL) {
180                 DEBUG(3, ("Could not find domain for sid %s\n",
181                           sid_string_static(sid)));
182                 goto error;
183         }
184
185         sid_copy(&s->user_sid, sid);
186
187         query_user_async(s->state->mem_ctx, s->domain, sid,
188                          getpwsid_queryuser_recv, s);
189         return;
190
191  error:
192         request_error(state);
193 }
194         
195 static void getpwsid_queryuser_recv(void *private, BOOL success,
196                                     const char *acct_name,
197                                     const char *full_name, uint32 group_rid)
198 {
199         struct getpwsid_state *s =
200                 talloc_get_type_abort(private, struct getpwsid_state);
201
202         if (!success) {
203                 DEBUG(5, ("Could not query user %s\\%s\n", s->domain->name,
204                           s->username));
205                 request_error(s->state);
206                 return;
207         }
208
209         s->username = talloc_strdup(s->state->mem_ctx, acct_name);
210         s->fullname = talloc_strdup(s->state->mem_ctx, full_name);
211         sid_copy(&s->group_sid, &s->domain->sid);
212         sid_append_rid(&s->group_sid, group_rid);
213
214         winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid,
215                                getpwsid_sid2uid_recv, s);
216 }
217
218 static void getpwsid_sid2uid_recv(void *private, BOOL success, uid_t uid)
219 {
220         struct getpwsid_state *s =
221                 talloc_get_type_abort(private, struct getpwsid_state);
222
223         if (!success) {
224                 DEBUG(5, ("Could not query user's %s\\%s uid\n",
225                           s->domain->name, s->username));
226                 request_error(s->state);
227                 return;
228         }
229
230         s->uid = uid;
231         winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
232                                getpwsid_sid2gid_recv, s);
233 }
234
235 static void getpwsid_sid2gid_recv(void *private, BOOL success, gid_t gid)
236 {
237         struct getpwsid_state *s =
238                 talloc_get_type_abort(private, struct getpwsid_state);
239         struct winbindd_pw *pw;
240         fstring output_username;
241         char *homedir;
242         char *shell;
243
244         if (!success) {
245                 DEBUG(5, ("Could not query user's %s\\%s\n gid",
246                           s->domain->name, s->username));
247                 goto failed;
248         }
249
250         s->gid = gid;
251
252         pw = &s->state->response.data.pw;
253         pw->pw_uid = s->uid;
254         pw->pw_gid = s->gid;
255         fill_domain_username(output_username, s->domain->name, s->username); 
256         safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
257         safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1);
258
259         /* Home directory and shell - use template config parameters.  The
260            defaults are /tmp for the home directory and /bin/false for
261            shell. */
262         
263         /* The substitution of %U and %D in the 'template homedir' is done
264            by alloc_sub_specified() below. */
265
266         fstrcpy(current_user_info.domain, s->domain->name);
267
268         homedir = alloc_sub_specified(lp_template_homedir(), s->username,
269                                       s->domain->name, pw->pw_uid, pw->pw_gid);
270         if (homedir == NULL) {
271                 DEBUG(5, ("Could not compose homedir\n"));
272                 goto failed;
273         }
274         safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1);
275         SAFE_FREE(homedir);
276         
277         shell = alloc_sub_specified(lp_template_shell(), s->username,
278                                     s->domain->name, pw->pw_uid, pw->pw_gid);
279         if (shell == NULL) {
280                 DEBUG(5, ("Could not compose shell\n"));
281                 goto failed;
282         }
283         safe_strcpy(pw->pw_shell, shell, sizeof(pw->pw_shell) - 1);
284         SAFE_FREE(shell);
285
286         /* Password - set to "x" as we can't generate anything useful here.
287            Authentication can be done using the pam_winbind module. */
288
289         safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
290
291         request_ok(s->state);
292         return;
293
294  failed:
295         request_error(s->state);
296 }
297
298 /* Return a password structure from a username.  */
299
300 static void getpwnam_name2sid_recv(void *private, BOOL success,
301                                    const DOM_SID *sid, enum SID_NAME_USE type);
302
303 void winbindd_getpwnam(struct winbindd_cli_state *state)
304 {
305         struct winbindd_domain *domain;
306         fstring domname, username;
307
308         /* Ensure null termination */
309         state->request.data.username[sizeof(state->request.data.username)-1]='\0';
310
311         DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state->pid,
312                   state->request.data.username));
313
314         if (!parse_domain_user(state->request.data.username, domname,
315                                username)) {
316                 DEBUG(0, ("Could not parse domain user: %s\n",
317                           state->request.data.username));
318                 request_error(state);
319                 return;
320         }
321         
322         /* Get info for the domain */
323
324         domain = find_domain_from_name(domname);
325
326         if (domain == NULL) {
327                 DEBUG(7, ("could not find domain entry for domain %s\n",
328                           domname));
329                 request_error(state);
330                 return;
331         }
332
333         if ( strequal(domname, lp_workgroup()) && lp_winbind_trusted_domains_only() ) {
334                 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getpwnam() for %s\\%s.\n", 
335                         domname, username));
336                 request_error(state);
337                 return;
338         }       
339
340         /* Get rid and name type from name.  The following costs 1 packet */
341
342         winbindd_lookupname_async(state->mem_ctx, domname, username,
343                                   getpwnam_name2sid_recv, state);
344 }
345
346 static void getpwnam_name2sid_recv(void *private, BOOL success,
347                                    const DOM_SID *sid, enum SID_NAME_USE type)
348 {
349         struct winbindd_cli_state *state = private;
350
351         if (!success) {
352                 DEBUG(5, ("Could not lookup name for user %s\n",
353                           state->request.data.username));
354                 request_error(state);
355                 return;
356         }
357
358         if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) {
359                 DEBUG(5, ("%s is not a user\n", state->request.data.username));
360                 request_error(state);
361                 return;
362         }
363
364         winbindd_getpwsid(state, sid);
365 }
366
367 /* Return a password structure given a uid number */
368
369 void winbindd_getpwuid(struct winbindd_cli_state *state)
370 {
371         DOM_SID user_sid;
372         NTSTATUS status;
373         
374         /* Bug out if the uid isn't in the winbind range */
375
376         if ((state->request.data.uid < server_state.uid_low ) ||
377             (state->request.data.uid > server_state.uid_high)) {
378                 request_error(state);
379                 return;
380         }
381
382         DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state->pid, 
383                   (unsigned long)state->request.data.uid));
384
385         status = idmap_uid_to_sid(&user_sid, state->request.data.uid,
386                                   ID_QUERY_ONLY | ID_CACHE_ONLY);
387
388         if (!NT_STATUS_IS_OK(status)) {
389                 DEBUG(5, ("Could not find SID for uid %lu\n",
390                           (unsigned long)state->request.data.uid));
391                 request_error(state);
392                 return;
393         }
394
395         winbindd_getpwsid(state, &user_sid);
396 }
397
398 /*
399  * set/get/endpwent functions
400  */
401
402 /* Rewind file pointer for ntdom passwd database */
403
404 static BOOL winbindd_setpwent_internal(struct winbindd_cli_state *state)
405 {
406         struct winbindd_domain *domain;
407         
408         DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
409         
410         /* Check user has enabled this */
411         
412         if (!lp_winbind_enum_users()) {
413                 return False;
414         }
415
416         /* Free old static data if it exists */
417         
418         if (state->getpwent_state != NULL) {
419                 free_getent_state(state->getpwent_state);
420                 state->getpwent_state = NULL;
421         }
422
423 #if 0   /* JERRY */
424         /* add any local users we have */
425                 
426         if ( (domain_state = (struct getent_state *)malloc(sizeof(struct getent_state))) == NULL )
427                 return False;
428                 
429         ZERO_STRUCTP(domain_state);
430
431         /* Add to list of open domains */
432                 
433         DLIST_ADD(state->getpwent_state, domain_state);
434 #endif
435         
436         /* Create sam pipes for each domain we know about */
437         
438         for(domain = domain_list(); domain != NULL; domain = domain->next) {
439                 struct getent_state *domain_state;
440                 
441                 
442                 /* don't add our domaina if we are a PDC or if we 
443                    are a member of a Samba domain */
444                 
445                 if ( (IS_DC || lp_winbind_trusted_domains_only())
446                         && strequal(domain->name, lp_workgroup()) )
447                 {
448                         continue;
449                 }
450                                                 
451                 /* Create a state record for this domain */
452                 
453                 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
454                         DEBUG(0, ("malloc failed\n"));
455                         return False;
456                 }
457                 
458                 ZERO_STRUCTP(domain_state);
459
460                 fstrcpy(domain_state->domain_name, domain->name);
461
462                 /* Add to list of open domains */
463                 
464                 DLIST_ADD(state->getpwent_state, domain_state);
465         }
466         
467         state->getpwent_initialized = True;
468         return True;
469 }
470
471 void winbindd_setpwent(struct winbindd_cli_state *state)
472 {
473         if (winbindd_setpwent_internal(state)) {
474                 request_ok(state);
475         } else {
476                 request_error(state);
477         }
478 }
479
480 /* Close file pointer to ntdom passwd database */
481
482 void winbindd_endpwent(struct winbindd_cli_state *state)
483 {
484         DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
485
486         free_getent_state(state->getpwent_state);    
487         state->getpwent_initialized = False;
488         state->getpwent_state = NULL;
489         request_ok(state);
490 }
491
492 /* Get partial list of domain users for a domain.  We fill in the sam_entries,
493    and num_sam_entries fields with domain user information.  The dispinfo_ndx
494    field is incremented to the index of the next user to fetch.  Return True if
495    some users were returned, False otherwise. */
496
497 static BOOL get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
498 {
499         NTSTATUS status;
500         uint32 num_entries;
501         WINBIND_USERINFO *info;
502         struct getpwent_user *name_list = NULL;
503         BOOL result = False;
504         struct winbindd_domain *domain;
505         struct winbindd_methods *methods;
506         unsigned int i;
507
508         if (ent->num_sam_entries)
509                 return False;
510
511         if (!(domain = find_domain_from_name(ent->domain_name))) {
512                 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
513                           ent->domain_name));
514                 return False;
515         }
516
517         methods = domain->methods;
518
519         /* Free any existing user info */
520
521         SAFE_FREE(ent->sam_entries);
522         ent->num_sam_entries = 0;
523         
524         /* Call query_user_list to get a list of usernames and user rids */
525
526         num_entries = 0;
527
528         status = methods->query_user_list(domain, mem_ctx, &num_entries, 
529                                           &info);
530                 
531         if (num_entries) {
532                 struct getpwent_user *tnl;
533                 
534                 tnl = SMB_REALLOC_ARRAY(name_list, struct getpwent_user, ent->num_sam_entries + num_entries);
535                 
536                 if (!tnl) {
537                         DEBUG(0,("get_sam_user_entries realloc failed.\n"));
538                         SAFE_FREE(name_list);
539                         goto done;
540                 } else
541                         name_list = tnl;
542         }
543
544         for (i = 0; i < num_entries; i++) {
545                 /* Store account name and gecos */
546                 if (!info[i].acct_name) {
547                         fstrcpy(name_list[ent->num_sam_entries + i].name, "");
548                 } else {
549                         fstrcpy(name_list[ent->num_sam_entries + i].name, 
550                                 info[i].acct_name); 
551                 }
552                 if (!info[i].full_name) {
553                         fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
554                 } else {
555                         fstrcpy(name_list[ent->num_sam_entries + i].gecos, 
556                                 info[i].full_name); 
557                 }
558                 
559                 /* User and group ids */
560                 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
561                          &info[i].user_sid);
562                 sid_copy(&name_list[ent->num_sam_entries+i].group_sid,
563                          &info[i].group_sid);
564         }
565                 
566         ent->num_sam_entries += num_entries;
567         
568         /* Fill in remaining fields */
569         
570         ent->sam_entries = name_list;
571         ent->sam_entry_index = 0;
572         result = ent->num_sam_entries > 0;
573
574  done:
575
576         return result;
577 }
578
579 /* Fetch next passwd entry from ntdom database */
580
581 #define MAX_GETPWENT_USERS 500
582
583 void winbindd_getpwent(struct winbindd_cli_state *state)
584 {
585         struct getent_state *ent;
586         struct winbindd_pw *user_list;
587         int num_users, user_list_ndx = 0, i;
588
589         DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
590
591         /* Check user has enabled this */
592
593         if (!lp_winbind_enum_users()) {
594                 request_error(state);
595                 return;
596         }
597
598         /* Allocate space for returning a chunk of users */
599
600         num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
601         
602         if ((state->response.extra_data = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users)) == NULL) {
603                 request_error(state);
604                 return;
605         }
606
607         memset(state->response.extra_data, 0, num_users * 
608                sizeof(struct winbindd_pw));
609
610         user_list = (struct winbindd_pw *)state->response.extra_data;
611
612         if (!state->getpwent_initialized)
613                 winbindd_setpwent_internal(state);
614         
615         if (!(ent = state->getpwent_state)) {
616                 request_error(state);
617                 return;
618         }
619
620         /* Start sending back users */
621
622         for (i = 0; i < num_users; i++) {
623                 struct getpwent_user *name_list = NULL;
624                 uint32 result;
625
626                 /* Do we need to fetch another chunk of users? */
627
628                 if (ent->num_sam_entries == ent->sam_entry_index) {
629
630                         while(ent &&
631                               !get_sam_user_entries(ent, state->mem_ctx)) {
632                                 struct getent_state *next_ent;
633
634                                 /* Free state information for this domain */
635
636                                 SAFE_FREE(ent->sam_entries);
637
638                                 next_ent = ent->next;
639                                 DLIST_REMOVE(state->getpwent_state, ent);
640
641                                 SAFE_FREE(ent);
642                                 ent = next_ent;
643                         }
644  
645                         /* No more domains */
646
647                         if (!ent) 
648                                 break;
649                 }
650
651                 name_list = ent->sam_entries;
652
653                 /* Lookup user info */
654                 
655                 result = winbindd_fill_pwent(
656                         ent->domain_name, 
657                         name_list[ent->sam_entry_index].name,
658                         &name_list[ent->sam_entry_index].user_sid,
659                         &name_list[ent->sam_entry_index].group_sid,
660                         name_list[ent->sam_entry_index].gecos,
661                         &user_list[user_list_ndx]);
662                 
663                 ent->sam_entry_index++;
664                 
665                 /* Add user to return list */
666                 
667                 if (result) {
668                                 
669                         user_list_ndx++;
670                         state->response.data.num_entries++;
671                         state->response.length += 
672                                 sizeof(struct winbindd_pw);
673
674                 } else
675                         DEBUG(1, ("could not lookup domain user %s\n",
676                                   name_list[ent->sam_entry_index].name));
677         }
678
679         /* Out of domains */
680
681         if (user_list_ndx > 0)
682                 request_ok(state);
683         else
684                 request_error(state);
685 }
686
687 /* List domain users without mapping to unix ids */
688
689 void winbindd_list_users(struct winbindd_cli_state *state)
690 {
691         struct winbindd_domain *domain;
692         WINBIND_USERINFO *info;
693         const char *which_domain;
694         uint32 num_entries = 0, total_entries = 0;
695         char *ted, *extra_data = NULL;
696         int extra_data_len = 0;
697         enum winbindd_result rv = WINBINDD_ERROR;
698
699         DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid));
700
701         /* Ensure null termination */
702         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';  
703         which_domain = state->request.domain_name;
704         
705         /* Enumerate over trusted domains */
706
707         for (domain = domain_list(); domain; domain = domain->next) {
708                 NTSTATUS status;
709                 struct winbindd_methods *methods;
710                 unsigned int i;
711                 
712                 /* if we have a domain name restricting the request and this
713                    one in the list doesn't match, then just bypass the remainder
714                    of the loop */
715                    
716                 if ( *which_domain && !strequal(which_domain, domain->name) )
717                         continue;
718                         
719                 methods = domain->methods;
720
721                 /* Query display info */
722                 status = methods->query_user_list(domain, state->mem_ctx, 
723                                                   &num_entries, &info);
724
725                 if (num_entries == 0)
726                         continue;
727
728                 /* Allocate some memory for extra data */
729                 total_entries += num_entries;
730                         
731                 ted = SMB_REALLOC(extra_data, sizeof(fstring) * total_entries);
732                         
733                 if (!ted) {
734                         DEBUG(0,("failed to enlarge buffer!\n"));
735                         SAFE_FREE(extra_data);
736                         goto done;
737                 } else 
738                         extra_data = ted;
739                         
740                 /* Pack user list into extra data fields */
741                         
742                 for (i = 0; i < num_entries; i++) {
743                         fstring acct_name, name;
744                         
745                         if (!info[i].acct_name) {
746                                 fstrcpy(acct_name, "");
747                         } else {
748                                 fstrcpy(acct_name, info[i].acct_name);
749                         }
750                         
751                         fill_domain_username(name, domain->name, acct_name);
752                         
753                                 /* Append to extra data */
754                         memcpy(&extra_data[extra_data_len], name, 
755                                strlen(name));
756                         extra_data_len += strlen(name);
757                         extra_data[extra_data_len++] = ',';
758                 }   
759         }
760
761         /* Assign extra_data fields in response structure */
762
763         if (extra_data) {
764                 extra_data[extra_data_len - 1] = '\0';
765                 state->response.extra_data = extra_data;
766                 state->response.length += extra_data_len;
767         }
768
769         /* No domains responded but that's still OK so don't return an
770            error. */
771
772         rv = WINBINDD_OK;
773
774  done:
775
776         if (rv == WINBINDD_OK)
777                 request_ok(state);
778         else
779                 request_error(state);
780 }