[GLUE] Rsync SAMBA_3_0 SVN r25598 in order to create the v3-0-test branch.
[Samba.git] / source / nsswitch / winbindd_user.c
blob18797772dd406bbf4b5411a30460af2af0009334
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 fstrcpy( username, acct_name );
243 strlower_m( username );
244 s->username = talloc_strdup(s->state->mem_ctx, username);
246 ws_name_replace( s->username, WB_REPLACE_CHAR );
248 s->fullname = talloc_strdup(s->state->mem_ctx, full_name);
249 s->homedir = talloc_strdup(s->state->mem_ctx, homedir);
250 s->shell = talloc_strdup(s->state->mem_ctx, shell);
251 s->gid = gid;
252 sid_copy(&s->group_sid, &s->domain->sid);
253 sid_append_rid(&s->group_sid, group_rid);
255 winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid,
256 getpwsid_sid2uid_recv, s);
259 static void getpwsid_sid2uid_recv(void *private_data, BOOL success, uid_t uid)
261 struct getpwsid_state *s =
262 talloc_get_type_abort(private_data, struct getpwsid_state);
264 if (!success) {
265 DEBUG(5, ("Could not query uid for user %s\\%s\n",
266 s->domain->name, s->username));
267 request_error(s->state);
268 return;
271 s->uid = uid;
272 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
273 getpwsid_sid2gid_recv, s);
276 static void getpwsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
278 struct getpwsid_state *s =
279 talloc_get_type_abort(private_data, struct getpwsid_state);
280 struct winbindd_pw *pw;
281 fstring output_username;
283 /* allow the nss backend to override the primary group ID.
284 If the gid has already been set, then keep it.
285 This makes me feel dirty. If the nss backend already
286 gave us a gid, we don't really care whether the sid2gid()
287 call worked or not. --jerry */
289 if ( s->gid == (gid_t)-1 ) {
291 if (!success) {
292 DEBUG(5, ("Could not query gid for user %s\\%s\n",
293 s->domain->name, s->username));
294 goto failed;
297 /* take what the sid2gid() call gave us */
298 s->gid = gid;
301 pw = &s->state->response.data.pw;
302 pw->pw_uid = s->uid;
303 pw->pw_gid = s->gid;
304 fill_domain_username(output_username, s->domain->name, s->username, True);
305 safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
306 safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1);
308 if (!fillup_pw_field(lp_template_homedir(), s->username, s->domain->name,
309 pw->pw_uid, pw->pw_gid, s->homedir, pw->pw_dir)) {
310 DEBUG(5, ("Could not compose homedir\n"));
311 goto failed;
314 if (!fillup_pw_field(lp_template_shell(), s->username, s->domain->name,
315 pw->pw_uid, pw->pw_gid, s->shell, pw->pw_shell)) {
316 DEBUG(5, ("Could not compose shell\n"));
317 goto failed;
320 /* Password - set to "*" as we can't generate anything useful here.
321 Authentication can be done using the pam_winbind module. */
323 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
325 request_ok(s->state);
326 return;
328 failed:
329 request_error(s->state);
332 /* Return a password structure from a username. */
334 static void getpwnam_name2sid_recv(void *private_data, BOOL success,
335 const DOM_SID *sid, enum lsa_SidType type);
337 void winbindd_getpwnam(struct winbindd_cli_state *state)
339 struct winbindd_domain *domain;
340 fstring domname, username;
342 /* Ensure null termination */
343 state->request.data.username[sizeof(state->request.data.username)-1]='\0';
345 DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state->pid,
346 state->request.data.username));
348 ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
350 if (!parse_domain_user(state->request.data.username, domname,
351 username)) {
352 DEBUG(5, ("Could not parse domain user: %s\n",
353 state->request.data.username));
354 request_error(state);
355 return;
358 /* Get info for the domain */
360 domain = find_domain_from_name(domname);
362 if (domain == NULL) {
363 DEBUG(7, ("could not find domain entry for domain %s\n",
364 domname));
365 request_error(state);
366 return;
369 if ( strequal(domname, lp_workgroup()) && lp_winbind_trusted_domains_only() ) {
370 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getpwnam() for %s\\%s.\n",
371 domname, username));
372 request_error(state);
373 return;
376 /* Get rid and name type from name. The following costs 1 packet */
378 winbindd_lookupname_async(state->mem_ctx, domname, username,
379 getpwnam_name2sid_recv, state);
382 static void getpwnam_name2sid_recv(void *private_data, BOOL success,
383 const DOM_SID *sid, enum lsa_SidType type)
385 struct winbindd_cli_state *state =
386 (struct winbindd_cli_state *)private_data;
388 if (!success) {
389 DEBUG(5, ("Could not lookup name for user %s\n",
390 state->request.data.username));
391 request_error(state);
392 return;
395 if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) {
396 DEBUG(5, ("%s is not a user\n", state->request.data.username));
397 request_error(state);
398 return;
401 winbindd_getpwsid(state, sid);
404 static void getpwuid_recv(void *private_data, BOOL success, const char *sid)
406 struct winbindd_cli_state *state =
407 (struct winbindd_cli_state *)private_data;
408 DOM_SID user_sid;
410 if (!success) {
411 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
412 (unsigned long)(state->request.data.uid)));
413 request_error(state);
414 return;
417 DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
418 (unsigned long)(state->request.data.uid), sid));
420 string_to_sid(&user_sid, sid);
421 winbindd_getpwsid(state, &user_sid);
424 /* Return a password structure given a uid number */
425 void winbindd_getpwuid(struct winbindd_cli_state *state)
427 DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state->pid,
428 (unsigned long)state->request.data.uid));
430 /* always query idmap via the async interface */
431 /* if this turns to be too slow we will add here a direct query to the cache */
432 winbindd_uid2sid_async(state->mem_ctx, state->request.data.uid, getpwuid_recv, state);
436 * set/get/endpwent functions
439 /* Rewind file pointer for ntdom passwd database */
441 static BOOL winbindd_setpwent_internal(struct winbindd_cli_state *state)
443 struct winbindd_domain *domain;
445 DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
447 /* Check user has enabled this */
449 if (!lp_winbind_enum_users()) {
450 return False;
453 /* Free old static data if it exists */
455 if (state->getpwent_state != NULL) {
456 free_getent_state(state->getpwent_state);
457 state->getpwent_state = NULL;
460 #if 0 /* JERRY */
461 /* add any local users we have */
463 if ( (domain_state = (struct getent_state *)malloc(sizeof(struct getent_state))) == NULL )
464 return False;
466 ZERO_STRUCTP(domain_state);
468 /* Add to list of open domains */
470 DLIST_ADD(state->getpwent_state, domain_state);
471 #endif
473 /* Create sam pipes for each domain we know about */
475 for(domain = domain_list(); domain != NULL; domain = domain->next) {
476 struct getent_state *domain_state;
479 /* don't add our domaina if we are a PDC or if we
480 are a member of a Samba domain */
482 if ( (IS_DC || lp_winbind_trusted_domains_only())
483 && strequal(domain->name, lp_workgroup()) )
485 continue;
488 /* Create a state record for this domain */
490 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
491 DEBUG(0, ("malloc failed\n"));
492 return False;
495 ZERO_STRUCTP(domain_state);
497 fstrcpy(domain_state->domain_name, domain->name);
499 /* Add to list of open domains */
501 DLIST_ADD(state->getpwent_state, domain_state);
504 state->getpwent_initialized = True;
505 return True;
508 void winbindd_setpwent(struct winbindd_cli_state *state)
510 if (winbindd_setpwent_internal(state)) {
511 request_ok(state);
512 } else {
513 request_error(state);
517 /* Close file pointer to ntdom passwd database */
519 void winbindd_endpwent(struct winbindd_cli_state *state)
521 DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
523 free_getent_state(state->getpwent_state);
524 state->getpwent_initialized = False;
525 state->getpwent_state = NULL;
526 request_ok(state);
529 /* Get partial list of domain users for a domain. We fill in the sam_entries,
530 and num_sam_entries fields with domain user information. The dispinfo_ndx
531 field is incremented to the index of the next user to fetch. Return True if
532 some users were returned, False otherwise. */
534 static BOOL get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
536 NTSTATUS status;
537 uint32 num_entries;
538 WINBIND_USERINFO *info;
539 struct getpwent_user *name_list = NULL;
540 struct winbindd_domain *domain;
541 struct winbindd_methods *methods;
542 unsigned int i;
544 if (ent->num_sam_entries)
545 return False;
547 if (!(domain = find_domain_from_name(ent->domain_name))) {
548 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
549 ent->domain_name));
550 return False;
553 methods = domain->methods;
555 /* Free any existing user info */
557 SAFE_FREE(ent->sam_entries);
558 ent->num_sam_entries = 0;
560 /* Call query_user_list to get a list of usernames and user rids */
562 num_entries = 0;
564 status = methods->query_user_list(domain, mem_ctx, &num_entries,
565 &info);
567 if (!NT_STATUS_IS_OK(status)) {
568 DEBUG(10,("get_sam_user_entries: query_user_list failed with %s\n",
569 nt_errstr(status) ));
570 return False;
573 if (num_entries) {
574 name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user, ent->num_sam_entries + num_entries);
576 if (!name_list) {
577 DEBUG(0,("get_sam_user_entries realloc failed.\n"));
578 return False;
582 for (i = 0; i < num_entries; i++) {
583 /* Store account name and gecos */
584 if (!info[i].acct_name) {
585 fstrcpy(name_list[ent->num_sam_entries + i].name, "");
586 } else {
587 fstrcpy(name_list[ent->num_sam_entries + i].name,
588 info[i].acct_name);
590 if (!info[i].full_name) {
591 fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
592 } else {
593 fstrcpy(name_list[ent->num_sam_entries + i].gecos,
594 info[i].full_name);
596 if (!info[i].homedir) {
597 fstrcpy(name_list[ent->num_sam_entries + i].homedir, "");
598 } else {
599 fstrcpy(name_list[ent->num_sam_entries + i].homedir,
600 info[i].homedir);
602 if (!info[i].shell) {
603 fstrcpy(name_list[ent->num_sam_entries + i].shell, "");
604 } else {
605 fstrcpy(name_list[ent->num_sam_entries + i].shell,
606 info[i].shell);
610 /* User and group ids */
611 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
612 &info[i].user_sid);
613 sid_copy(&name_list[ent->num_sam_entries+i].group_sid,
614 &info[i].group_sid);
617 ent->num_sam_entries += num_entries;
619 /* Fill in remaining fields */
621 ent->sam_entries = name_list;
622 ent->sam_entry_index = 0;
623 return ent->num_sam_entries > 0;
626 /* Fetch next passwd entry from ntdom database */
628 #define MAX_GETPWENT_USERS 500
630 void winbindd_getpwent(struct winbindd_cli_state *state)
632 struct getent_state *ent;
633 struct winbindd_pw *user_list;
634 int num_users, user_list_ndx;
636 DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
638 /* Check user has enabled this */
640 if (!lp_winbind_enum_users()) {
641 request_error(state);
642 return;
645 /* Allocate space for returning a chunk of users */
647 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
649 if (num_users == 0) {
650 request_error(state);
651 return;
654 if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users)) == NULL) {
655 request_error(state);
656 return;
659 memset(state->response.extra_data.data, 0, num_users *
660 sizeof(struct winbindd_pw));
662 user_list = (struct winbindd_pw *)state->response.extra_data.data;
664 if (!state->getpwent_initialized)
665 winbindd_setpwent_internal(state);
667 if (!(ent = state->getpwent_state)) {
668 request_error(state);
669 return;
672 /* Start sending back users */
674 for (user_list_ndx = 0; user_list_ndx < num_users; ) {
675 struct getpwent_user *name_list = NULL;
676 uint32 result;
678 /* Do we need to fetch another chunk of users? */
680 if (ent->num_sam_entries == ent->sam_entry_index) {
682 while(ent &&
683 !get_sam_user_entries(ent, state->mem_ctx)) {
684 struct getent_state *next_ent;
686 /* Free state information for this domain */
688 SAFE_FREE(ent->sam_entries);
690 next_ent = ent->next;
691 DLIST_REMOVE(state->getpwent_state, ent);
693 SAFE_FREE(ent);
694 ent = next_ent;
697 /* No more domains */
699 if (!ent)
700 break;
703 name_list = (struct getpwent_user *)ent->sam_entries;
705 /* Lookup user info */
707 result = winbindd_fill_pwent(
708 ent->domain_name,
709 name_list[ent->sam_entry_index].name,
710 &name_list[ent->sam_entry_index].user_sid,
711 &name_list[ent->sam_entry_index].group_sid,
712 name_list[ent->sam_entry_index].gecos,
713 name_list[ent->sam_entry_index].homedir,
714 name_list[ent->sam_entry_index].shell,
715 &user_list[user_list_ndx]);
717 /* Add user to return list */
719 if (result) {
721 user_list_ndx++;
722 state->response.data.num_entries++;
723 state->response.length +=
724 sizeof(struct winbindd_pw);
726 } else
727 DEBUG(1, ("could not lookup domain user %s\n",
728 name_list[ent->sam_entry_index].name));
730 ent->sam_entry_index++;
734 /* Out of domains */
736 if (user_list_ndx > 0)
737 request_ok(state);
738 else
739 request_error(state);
742 /* List domain users without mapping to unix ids */
744 void winbindd_list_users(struct winbindd_cli_state *state)
746 struct winbindd_domain *domain;
747 WINBIND_USERINFO *info;
748 const char *which_domain;
749 uint32 num_entries = 0, total_entries = 0;
750 char *extra_data = NULL;
751 int extra_data_len = 0;
752 enum winbindd_result rv = WINBINDD_ERROR;
754 DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid));
756 /* Ensure null termination */
757 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
758 which_domain = state->request.domain_name;
760 /* Enumerate over trusted domains */
762 for (domain = domain_list(); domain; domain = domain->next) {
763 NTSTATUS status;
764 struct winbindd_methods *methods;
765 unsigned int i;
767 /* if we have a domain name restricting the request and this
768 one in the list doesn't match, then just bypass the remainder
769 of the loop */
771 if ( *which_domain && !strequal(which_domain, domain->name) )
772 continue;
774 methods = domain->methods;
776 /* Query display info */
777 status = methods->query_user_list(domain, state->mem_ctx,
778 &num_entries, &info);
780 if (!NT_STATUS_IS_OK(status)) {
781 continue;
784 if (num_entries == 0)
785 continue;
787 /* Allocate some memory for extra data */
788 total_entries += num_entries;
790 extra_data = (char *)SMB_REALLOC(
791 extra_data, sizeof(fstring) * total_entries);
793 if (!extra_data) {
794 DEBUG(0,("failed to enlarge buffer!\n"));
795 goto done;
798 /* Pack user list into extra data fields */
800 for (i = 0; i < num_entries; i++) {
801 fstring acct_name, name;
803 if (!info[i].acct_name) {
804 fstrcpy(acct_name, "");
805 } else {
806 fstrcpy(acct_name, info[i].acct_name);
809 fill_domain_username(name, domain->name, acct_name, True);
811 /* Append to extra data */
812 memcpy(&extra_data[extra_data_len], name,
813 strlen(name));
814 extra_data_len += strlen(name);
815 extra_data[extra_data_len++] = ',';
819 /* Assign extra_data fields in response structure */
821 if (extra_data) {
822 extra_data[extra_data_len - 1] = '\0';
823 state->response.extra_data.data = extra_data;
824 state->response.length += extra_data_len;
827 /* No domains responded but that's still OK so don't return an
828 error. */
830 rv = WINBINDD_OK;
832 done:
834 if (rv == WINBINDD_OK)
835 request_ok(state);
836 else
837 request_error(state);