2 Unix SMB/CIFS implementation.
4 Winbind daemon - user related functions
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jeremy Allison 2001.
8 Copyright (C) Gerald (Jerry) Carter 2003.
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #define DBGC_CLASS DBGC_WINBIND
30 static bool fillup_pw_field(const char *lp_template
,
43 /* The substitution of %U and %D in the 'template
44 homedir' is done by talloc_sub_specified() below.
45 If we have an in string (which means the value has already
46 been set in the nss_info backend), then use that.
47 Otherwise use the template value passed in. */
49 if ( in
&& !strequal(in
,"") && lp_security() == SEC_ADS
) {
50 templ
= talloc_sub_specified(NULL
, in
,
54 templ
= talloc_sub_specified(NULL
, lp_template
,
62 safe_strcpy(out
, templ
, sizeof(fstring
) - 1);
68 /* Fill a pwent structure with information we have obtained */
70 static bool winbindd_fill_pwent(char *dom_name
, char *user_name
,
71 DOM_SID
*user_sid
, DOM_SID
*group_sid
,
72 char *full_name
, char *homedir
, char *shell
,
73 struct winbindd_pw
*pw
)
75 fstring output_username
;
77 if (!pw
|| !dom_name
|| !user_name
)
80 /* Resolve the uid number */
82 if (!NT_STATUS_IS_OK(idmap_sid_to_uid(user_sid
, &pw
->pw_uid
))) {
83 DEBUG(1, ("error getting user id for sid %s\n",
84 sid_string_dbg(user_sid
)));
88 /* Resolve the gid number */
90 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(group_sid
, &pw
->pw_gid
))) {
91 DEBUG(1, ("error getting group id for sid %s\n",
92 sid_string_dbg(group_sid
)));
96 strlower_m(user_name
);
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
))
114 if (!fillup_pw_field(lp_template_shell(), user_name
, dom_name
,
115 pw
->pw_uid
, pw
->pw_gid
, shell
, pw
->pw_shell
))
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);
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
)
132 WINBIND_USERINFO user_info
;
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
,
148 if (!NT_STATUS_IS_OK(status
)) {
149 DEBUG(1, ("error getting user info for sid %s\n",
150 sid_string_dbg(&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_dbg(&sid
)));
163 return WINBINDD_ERROR
;
169 struct getpwsid_state
{
170 struct winbindd_cli_state
*state
;
171 struct winbindd_domain
*domain
;
182 static void getpwsid_queryuser_recv(void *private_data
, bool success
,
183 const char *acct_name
,
184 const char *full_name
,
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
,
195 struct getpwsid_state
*s
;
197 s
= TALLOC_ZERO_P(state
->mem_ctx
, struct getpwsid_state
);
199 DEBUG(0, ("talloc failed\n"));
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_dbg(sid
)));
211 sid_copy(&s
->user_sid
, sid
);
213 query_user_async(s
->state
->mem_ctx
, s
->domain
, sid
,
214 getpwsid_queryuser_recv
, s
);
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
,
230 struct getpwsid_state
*s
=
231 talloc_get_type_abort(private_data
, struct getpwsid_state
);
234 DEBUG(5, ("Could not query domain %s SID %s\n",
235 s
->domain
->name
, sid_string_dbg(&s
->user_sid
)));
236 request_error(s
->state
);
240 if ( acct_name
&& *acct_name
) {
241 fstrcpy( username
, acct_name
);
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 if (domain
== NULL
) {
250 DEBUG(5, ("find_lookup_domain_from_sid(%s) failed\n",
251 sid_string_dbg(&s
->user_sid
)));
252 request_error(s
->state
);
255 winbindd_lookup_name_by_sid(s
->state
->mem_ctx
, domain
,
256 &s
->user_sid
, &domain_name
,
259 /* If this still fails we ar4e done. Just error out */
261 DEBUG(5,("Could not obtain a name for SID %s\n",
262 sid_string_dbg(&s
->user_sid
)));
263 request_error(s
->state
);
267 fstrcpy( username
, user_name
);
270 strlower_m( username
);
271 s
->username
= talloc_strdup(s
->state
->mem_ctx
, username
);
273 ws_name_replace( s
->username
, WB_REPLACE_CHAR
);
275 s
->fullname
= talloc_strdup(s
->state
->mem_ctx
, full_name
);
276 s
->homedir
= talloc_strdup(s
->state
->mem_ctx
, homedir
);
277 s
->shell
= talloc_strdup(s
->state
->mem_ctx
, shell
);
279 sid_copy(&s
->group_sid
, &s
->domain
->sid
);
280 sid_append_rid(&s
->group_sid
, group_rid
);
282 winbindd_sid2uid_async(s
->state
->mem_ctx
, &s
->user_sid
,
283 getpwsid_sid2uid_recv
, s
);
286 static void getpwsid_sid2uid_recv(void *private_data
, bool success
, uid_t uid
)
288 struct getpwsid_state
*s
=
289 talloc_get_type_abort(private_data
, struct getpwsid_state
);
292 DEBUG(5, ("Could not query uid for user %s\\%s\n",
293 s
->domain
->name
, s
->username
));
294 request_error(s
->state
);
299 winbindd_sid2gid_async(s
->state
->mem_ctx
, &s
->group_sid
,
300 getpwsid_sid2gid_recv
, s
);
303 static void getpwsid_sid2gid_recv(void *private_data
, bool success
, gid_t gid
)
305 struct getpwsid_state
*s
=
306 talloc_get_type_abort(private_data
, struct getpwsid_state
);
307 struct winbindd_pw
*pw
;
308 fstring output_username
;
310 /* allow the nss backend to override the primary group ID.
311 If the gid has already been set, then keep it.
312 This makes me feel dirty. If the nss backend already
313 gave us a gid, we don't really care whether the sid2gid()
314 call worked or not. --jerry */
316 if ( s
->gid
== (gid_t
)-1 ) {
319 DEBUG(5, ("Could not query gid for user %s\\%s\n",
320 s
->domain
->name
, s
->username
));
324 /* take what the sid2gid() call gave us */
328 pw
= &s
->state
->response
.data
.pw
;
331 fill_domain_username(output_username
, s
->domain
->name
, s
->username
, True
);
332 safe_strcpy(pw
->pw_name
, output_username
, sizeof(pw
->pw_name
) - 1);
333 safe_strcpy(pw
->pw_gecos
, s
->fullname
, sizeof(pw
->pw_gecos
) - 1);
335 if (!fillup_pw_field(lp_template_homedir(), s
->username
, s
->domain
->name
,
336 pw
->pw_uid
, pw
->pw_gid
, s
->homedir
, pw
->pw_dir
)) {
337 DEBUG(5, ("Could not compose homedir\n"));
341 if (!fillup_pw_field(lp_template_shell(), s
->username
, s
->domain
->name
,
342 pw
->pw_uid
, pw
->pw_gid
, s
->shell
, pw
->pw_shell
)) {
343 DEBUG(5, ("Could not compose shell\n"));
347 /* Password - set to "*" as we can't generate anything useful here.
348 Authentication can be done using the pam_winbind module. */
350 safe_strcpy(pw
->pw_passwd
, "*", sizeof(pw
->pw_passwd
) - 1);
352 request_ok(s
->state
);
356 request_error(s
->state
);
359 /* Return a password structure from a username. */
361 static void getpwnam_name2sid_recv(void *private_data
, bool success
,
362 const DOM_SID
*sid
, enum lsa_SidType type
);
364 void winbindd_getpwnam(struct winbindd_cli_state
*state
)
366 struct winbindd_domain
*domain
;
367 fstring domname
, username
;
369 /* Ensure null termination */
370 state
->request
.data
.username
[sizeof(state
->request
.data
.username
)-1]='\0';
372 DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state
->pid
,
373 state
->request
.data
.username
));
375 ws_name_return( state
->request
.data
.username
, WB_REPLACE_CHAR
);
377 if (!parse_domain_user(state
->request
.data
.username
, domname
,
379 DEBUG(5, ("Could not parse domain user: %s\n",
380 state
->request
.data
.username
));
381 request_error(state
);
385 /* Get info for the domain */
387 domain
= find_domain_from_name(domname
);
389 if (domain
== NULL
) {
390 DEBUG(7, ("could not find domain entry for domain %s. "
391 "Using primary domain\n", domname
));
392 if ( (domain
= find_our_domain()) == NULL
) {
393 DEBUG(0,("Cannot find my primary domain structure!\n"));
394 request_error(state
);
399 if ( strequal(domname
, lp_workgroup()) && lp_winbind_trusted_domains_only() ) {
400 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getpwnam() for %s\\%s.\n",
402 request_error(state
);
406 /* Get rid and name type from name. The following costs 1 packet */
408 winbindd_lookupname_async(state
->mem_ctx
, domname
, username
,
409 getpwnam_name2sid_recv
, WINBINDD_GETPWNAM
,
413 static void getpwnam_name2sid_recv(void *private_data
, bool success
,
414 const DOM_SID
*sid
, enum lsa_SidType type
)
416 struct winbindd_cli_state
*state
=
417 (struct winbindd_cli_state
*)private_data
;
418 fstring domname
, username
;
421 DEBUG(5, ("Could not lookup name for user %s\n",
422 state
->request
.data
.username
));
423 request_error(state
);
427 if ((type
!= SID_NAME_USER
) && (type
!= SID_NAME_COMPUTER
)) {
428 DEBUG(5, ("%s is not a user\n", state
->request
.data
.username
));
429 request_error(state
);
433 if ( parse_domain_user(state
->request
.data
.username
, domname
, username
) ) {
434 check_domain_trusted( domname
, sid
);
439 winbindd_getpwsid(state
, sid
);
442 static void getpwuid_recv(void *private_data
, bool success
, const char *sid
)
444 struct winbindd_cli_state
*state
=
445 (struct winbindd_cli_state
*)private_data
;
449 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
450 (unsigned long)(state
->request
.data
.uid
)));
451 request_error(state
);
455 DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
456 (unsigned long)(state
->request
.data
.uid
), sid
));
458 string_to_sid(&user_sid
, sid
);
459 winbindd_getpwsid(state
, &user_sid
);
462 /* Return a password structure given a uid number */
463 void winbindd_getpwuid(struct winbindd_cli_state
*state
)
465 DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state
->pid
,
466 (unsigned long)state
->request
.data
.uid
));
468 /* always query idmap via the async interface */
469 /* if this turns to be too slow we will add here a direct query to the cache */
470 winbindd_uid2sid_async(state
->mem_ctx
, state
->request
.data
.uid
, getpwuid_recv
, state
);
474 * set/get/endpwent functions
477 /* Rewind file pointer for ntdom passwd database */
479 static bool winbindd_setpwent_internal(struct winbindd_cli_state
*state
)
481 struct winbindd_domain
*domain
;
483 DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state
->pid
));
485 /* Check user has enabled this */
487 if (!lp_winbind_enum_users()) {
491 /* Free old static data if it exists */
493 if (state
->getpwent_state
!= NULL
) {
494 free_getent_state(state
->getpwent_state
);
495 state
->getpwent_state
= NULL
;
499 /* add any local users we have */
501 if ( (domain_state
= (struct getent_state
*)malloc(sizeof(struct getent_state
))) == NULL
)
504 ZERO_STRUCTP(domain_state
);
506 /* Add to list of open domains */
508 DLIST_ADD(state
->getpwent_state
, domain_state
);
511 /* Create sam pipes for each domain we know about */
513 for(domain
= domain_list(); domain
!= NULL
; domain
= domain
->next
) {
514 struct getent_state
*domain_state
;
517 /* don't add our domaina if we are a PDC or if we
518 are a member of a Samba domain */
520 if ( (IS_DC
|| lp_winbind_trusted_domains_only())
521 && strequal(domain
->name
, lp_workgroup()) )
526 /* Create a state record for this domain */
528 if ((domain_state
= SMB_MALLOC_P(struct getent_state
)) == NULL
) {
529 DEBUG(0, ("malloc failed\n"));
533 ZERO_STRUCTP(domain_state
);
535 fstrcpy(domain_state
->domain_name
, domain
->name
);
537 /* Add to list of open domains */
539 DLIST_ADD(state
->getpwent_state
, domain_state
);
542 state
->getpwent_initialized
= True
;
546 void winbindd_setpwent(struct winbindd_cli_state
*state
)
548 if (winbindd_setpwent_internal(state
)) {
551 request_error(state
);
555 /* Close file pointer to ntdom passwd database */
557 void winbindd_endpwent(struct winbindd_cli_state
*state
)
559 DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state
->pid
));
561 free_getent_state(state
->getpwent_state
);
562 state
->getpwent_initialized
= False
;
563 state
->getpwent_state
= NULL
;
567 /* Get partial list of domain users for a domain. We fill in the sam_entries,
568 and num_sam_entries fields with domain user information. The dispinfo_ndx
569 field is incremented to the index of the next user to fetch. Return True if
570 some users were returned, False otherwise. */
572 static bool get_sam_user_entries(struct getent_state
*ent
, TALLOC_CTX
*mem_ctx
)
576 WINBIND_USERINFO
*info
;
577 struct getpwent_user
*name_list
= NULL
;
578 struct winbindd_domain
*domain
;
579 struct winbindd_methods
*methods
;
582 if (ent
->num_sam_entries
)
585 if (!(domain
= find_domain_from_name(ent
->domain_name
))) {
586 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
591 methods
= domain
->methods
;
593 /* Free any existing user info */
595 SAFE_FREE(ent
->sam_entries
);
596 ent
->num_sam_entries
= 0;
598 /* Call query_user_list to get a list of usernames and user rids */
602 status
= methods
->query_user_list(domain
, mem_ctx
, &num_entries
,
605 if (!NT_STATUS_IS_OK(status
)) {
606 DEBUG(10,("get_sam_user_entries: query_user_list failed with %s\n",
607 nt_errstr(status
) ));
612 name_list
= SMB_REALLOC_ARRAY(name_list
, struct getpwent_user
, ent
->num_sam_entries
+ num_entries
);
615 DEBUG(0,("get_sam_user_entries realloc failed.\n"));
620 for (i
= 0; i
< num_entries
; i
++) {
621 /* Store account name and gecos */
622 if (!info
[i
].acct_name
) {
623 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].name
, "");
625 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].name
,
628 if (!info
[i
].full_name
) {
629 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].gecos
, "");
631 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].gecos
,
634 if (!info
[i
].homedir
) {
635 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].homedir
, "");
637 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].homedir
,
640 if (!info
[i
].shell
) {
641 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].shell
, "");
643 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].shell
,
648 /* User and group ids */
649 sid_copy(&name_list
[ent
->num_sam_entries
+i
].user_sid
,
651 sid_copy(&name_list
[ent
->num_sam_entries
+i
].group_sid
,
655 ent
->num_sam_entries
+= num_entries
;
657 /* Fill in remaining fields */
659 ent
->sam_entries
= name_list
;
660 ent
->sam_entry_index
= 0;
661 return ent
->num_sam_entries
> 0;
664 /* Fetch next passwd entry from ntdom database */
666 #define MAX_GETPWENT_USERS 500
668 void winbindd_getpwent(struct winbindd_cli_state
*state
)
670 struct getent_state
*ent
;
671 struct winbindd_pw
*user_list
;
672 int num_users
, user_list_ndx
;
674 DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state
->pid
));
676 /* Check user has enabled this */
678 if (!lp_winbind_enum_users()) {
679 request_error(state
);
683 /* Allocate space for returning a chunk of users */
685 num_users
= MIN(MAX_GETPWENT_USERS
, state
->request
.data
.num_entries
);
687 if (num_users
== 0) {
688 request_error(state
);
692 if ((state
->response
.extra_data
.data
= SMB_MALLOC_ARRAY(struct winbindd_pw
, num_users
)) == NULL
) {
693 request_error(state
);
697 memset(state
->response
.extra_data
.data
, 0, num_users
*
698 sizeof(struct winbindd_pw
));
700 user_list
= (struct winbindd_pw
*)state
->response
.extra_data
.data
;
702 if (!state
->getpwent_initialized
)
703 winbindd_setpwent_internal(state
);
705 if (!(ent
= state
->getpwent_state
)) {
706 request_error(state
);
710 /* Start sending back users */
712 for (user_list_ndx
= 0; user_list_ndx
< num_users
; ) {
713 struct getpwent_user
*name_list
= NULL
;
716 /* Do we need to fetch another chunk of users? */
718 if (ent
->num_sam_entries
== ent
->sam_entry_index
) {
721 !get_sam_user_entries(ent
, state
->mem_ctx
)) {
722 struct getent_state
*next_ent
;
724 /* Free state information for this domain */
726 SAFE_FREE(ent
->sam_entries
);
728 next_ent
= ent
->next
;
729 DLIST_REMOVE(state
->getpwent_state
, ent
);
735 /* No more domains */
741 name_list
= (struct getpwent_user
*)ent
->sam_entries
;
743 /* Lookup user info */
745 result
= winbindd_fill_pwent(
747 name_list
[ent
->sam_entry_index
].name
,
748 &name_list
[ent
->sam_entry_index
].user_sid
,
749 &name_list
[ent
->sam_entry_index
].group_sid
,
750 name_list
[ent
->sam_entry_index
].gecos
,
751 name_list
[ent
->sam_entry_index
].homedir
,
752 name_list
[ent
->sam_entry_index
].shell
,
753 &user_list
[user_list_ndx
]);
755 /* Add user to return list */
760 state
->response
.data
.num_entries
++;
761 state
->response
.length
+=
762 sizeof(struct winbindd_pw
);
765 DEBUG(1, ("could not lookup domain user %s\n",
766 name_list
[ent
->sam_entry_index
].name
));
768 ent
->sam_entry_index
++;
774 if (user_list_ndx
> 0)
777 request_error(state
);
780 /* List domain users without mapping to unix ids */
782 void winbindd_list_users(struct winbindd_cli_state
*state
)
784 struct winbindd_domain
*domain
;
785 WINBIND_USERINFO
*info
;
786 const char *which_domain
;
787 uint32 num_entries
= 0, total_entries
= 0;
788 char *extra_data
= NULL
;
789 int extra_data_len
= 0;
790 enum winbindd_result rv
= WINBINDD_ERROR
;
792 DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state
->pid
));
794 /* Ensure null termination */
795 state
->request
.domain_name
[sizeof(state
->request
.domain_name
)-1]='\0';
796 which_domain
= state
->request
.domain_name
;
798 /* Enumerate over trusted domains */
800 for (domain
= domain_list(); domain
; domain
= domain
->next
) {
802 struct winbindd_methods
*methods
;
805 /* if we have a domain name restricting the request and this
806 one in the list doesn't match, then just bypass the remainder
809 if ( *which_domain
&& !strequal(which_domain
, domain
->name
) )
812 methods
= domain
->methods
;
814 /* Query display info */
815 status
= methods
->query_user_list(domain
, state
->mem_ctx
,
816 &num_entries
, &info
);
818 if (!NT_STATUS_IS_OK(status
)) {
822 if (num_entries
== 0)
825 /* Allocate some memory for extra data */
826 total_entries
+= num_entries
;
828 extra_data
= (char *)SMB_REALLOC(
829 extra_data
, sizeof(fstring
) * total_entries
);
832 DEBUG(0,("failed to enlarge buffer!\n"));
836 /* Pack user list into extra data fields */
838 for (i
= 0; i
< num_entries
; i
++) {
839 fstring acct_name
, name
;
841 if (!info
[i
].acct_name
) {
842 fstrcpy(acct_name
, "");
844 fstrcpy(acct_name
, info
[i
].acct_name
);
847 fill_domain_username(name
, domain
->name
, acct_name
, True
);
849 /* Append to extra data */
850 memcpy(&extra_data
[extra_data_len
], name
,
852 extra_data_len
+= strlen(name
);
853 extra_data
[extra_data_len
++] = ',';
857 /* Assign extra_data fields in response structure */
860 extra_data
[extra_data_len
- 1] = '\0';
861 state
->response
.extra_data
.data
= extra_data
;
862 state
->response
.length
+= extra_data_len
;
865 /* No domains responded but that's still OK so don't return an
872 if (rv
== WINBINDD_OK
)
875 request_error(state
);