2 Unix SMB/CIFS implementation.
4 Winbind daemon for ntdom nss module
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jeremy Allison 2001.
8 Copyright (C) Gerald (Jerry) Carter 2003.
9 Copyright (C) Volker Lendecke 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 extern bool opt_nocache
;
31 #define DBGC_CLASS DBGC_WINBIND
33 static void add_member(const char *domain
, const char *user
,
34 char **pp_members
, size_t *p_num_members
)
38 fill_domain_username(name
, domain
, user
, True
);
39 safe_strcat(name
, ",", sizeof(name
)-1);
40 string_append(pp_members
, name
);
44 /**********************************************************************
45 Add member users resulting from sid. Expand if it is a domain group.
46 **********************************************************************/
48 static void add_expanded_sid(const DOM_SID
*sid
, char **pp_members
, size_t *p_num_members
)
52 struct winbindd_domain
*domain
;
55 char *domain_name
= NULL
;
57 enum lsa_SidType type
;
66 TALLOC_CTX
*mem_ctx
= talloc_init("add_expanded_sid");
68 if (mem_ctx
== NULL
) {
69 DEBUG(1, ("talloc_init failed\n"));
73 sid_copy(&dom_sid
, sid
);
74 sid_split_rid(&dom_sid
, &rid
);
76 domain
= find_lookup_domain_from_sid(sid
);
79 DEBUG(3, ("Could not find domain for sid %s\n",
80 sid_string_dbg(sid
)));
84 result
= domain
->methods
->sid_to_name(domain
, mem_ctx
, sid
,
85 &domain_name
, &name
, &type
);
87 if (!NT_STATUS_IS_OK(result
)) {
88 DEBUG(3, ("sid_to_name failed for sid %s\n",
89 sid_string_dbg(sid
)));
93 DEBUG(10, ("Found name %s, type %d\n", name
, type
));
95 if (type
== SID_NAME_USER
) {
96 add_member(domain_name
, name
, pp_members
, p_num_members
);
100 if (type
!= SID_NAME_DOM_GRP
) {
101 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
106 /* Expand the domain group, this must be done via the target domain */
108 domain
= find_domain_from_sid(sid
);
110 if (domain
== NULL
) {
111 DEBUG(3, ("Could not find domain from SID %s\n",
112 sid_string_dbg(sid
)));
116 result
= domain
->methods
->lookup_groupmem(domain
, mem_ctx
,
121 if (!NT_STATUS_IS_OK(result
)) {
122 DEBUG(10, ("Could not lookup group members for %s: %s\n",
123 name
, nt_errstr(result
)));
127 for (i
=0; i
<num_names
; i
++) {
128 DEBUG(10, ("Adding group member SID %s\n",
129 sid_string_dbg(&sid_mem
[i
])));
131 if (types
[i
] != SID_NAME_USER
) {
132 DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
133 "Ignoring.\n", names
[i
], name
));
137 add_member(domain
->name
, names
[i
], pp_members
, p_num_members
);
141 talloc_destroy(mem_ctx
);
145 static bool fill_passdb_alias_grmem(struct winbindd_domain
*domain
,
147 size_t *num_gr_mem
, char **gr_mem
, size_t *gr_mem_len
)
150 size_t i
, num_members
;
156 if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid
, &members
,
160 for (i
=0; i
<num_members
; i
++) {
161 add_expanded_sid(&members
[i
], gr_mem
, num_gr_mem
);
164 TALLOC_FREE(members
);
166 if (*gr_mem
!= NULL
) {
169 /* We have at least one member, strip off the last "," */
170 len
= strlen(*gr_mem
);
171 (*gr_mem
)[len
-1] = '\0';
178 /* Fill a grent structure from various other information */
180 static bool fill_grent(struct winbindd_gr
*gr
, const char *dom_name
,
181 const char *gr_name
, gid_t unix_gid
)
183 fstring full_group_name
;
185 fill_domain_username( full_group_name
, dom_name
, gr_name
, True
);
187 gr
->gr_gid
= unix_gid
;
189 /* Group name and password */
191 safe_strcpy(gr
->gr_name
, full_group_name
, sizeof(gr
->gr_name
) - 1);
192 safe_strcpy(gr
->gr_passwd
, "x", sizeof(gr
->gr_passwd
) - 1);
197 /***********************************************************************
198 If "enum users" is set to false, and the group being looked
199 up is the Domain Users SID: S-1-5-domain-513, then for the
200 list of members check if the querying user is in that group,
201 and if so only return that user as the gr_mem array.
202 We can change this to a different parameter than "enum users"
203 if neccessaey, or parameterize the group list we do this for.
204 ***********************************************************************/
206 static bool fill_grent_mem_domusers( TALLOC_CTX
*mem_ctx
,
207 struct winbindd_domain
*domain
,
208 struct winbindd_cli_state
*state
,
210 enum lsa_SidType group_name_type
,
211 size_t *num_gr_mem
, char **gr_mem
,
214 DOM_SID querying_user_sid
;
215 DOM_SID
*pquerying_user_sid
= NULL
;
216 uint32 num_groups
= 0;
217 DOM_SID
*user_sids
= NULL
;
218 bool u_in_group
= False
;
221 unsigned int buf_len
= 0;
224 DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
228 uid_t ret_uid
= (uid_t
)-1;
229 if (sys_getpeereid(state
->sock
, &ret_uid
)==0) {
230 /* We know who's asking - look up their SID if
231 it's one we've mapped before. */
232 status
= idmap_uid_to_sid(&querying_user_sid
, ret_uid
);
233 if (NT_STATUS_IS_OK(status
)) {
234 pquerying_user_sid
= &querying_user_sid
;
235 DEBUG(10,("fill_grent_mem_domain_users: querying uid %u -> %s\n",
236 (unsigned int)ret_uid
,
237 sid_string_dbg(pquerying_user_sid
)));
242 /* Only look up if it was a winbindd user in this domain. */
243 if (pquerying_user_sid
&&
244 (sid_compare_domain(pquerying_user_sid
, &domain
->sid
) == 0)) {
246 DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n",
247 sid_string_dbg(pquerying_user_sid
) ));
249 status
= domain
->methods
->lookup_usergroups(domain
,
254 if (!NT_STATUS_IS_OK(status
)) {
255 DEBUG(1, ("fill_grent_mem_domain_users: lookup_usergroups failed "
256 "for sid %s in domain %s (error: %s)\n",
257 sid_string_dbg(pquerying_user_sid
),
263 for (i
= 0; i
< num_groups
; i
++) {
264 if (sid_equal(group_sid
, &user_sids
[i
])) {
265 /* User is in Domain Users, add their name
266 as the only group member. */
275 char *domainname
= NULL
;
276 char *username
= NULL
;
278 enum lsa_SidType type
;
280 DEBUG(10,("fill_grent_mem_domain_users: sid %s in 'Domain Users' in domain %s\n",
281 sid_string_dbg(pquerying_user_sid
),
284 status
= domain
->methods
->sid_to_name(domain
, mem_ctx
,
289 if (!NT_STATUS_IS_OK(status
)) {
290 DEBUG(1, ("could not lookup username for user "
291 "sid %s in domain %s (error: %s)\n",
292 sid_string_dbg(pquerying_user_sid
),
297 fill_domain_username(name
, domain
->name
, username
, True
);
300 if (!(buf
= (char *)SMB_MALLOC(buf_len
))) {
301 DEBUG(1, ("out of memory\n"));
304 memcpy(buf
, name
, buf_len
);
306 DEBUG(10,("fill_grent_mem_domain_users: user %s in "
307 "'Domain Users' in domain %s\n",
308 name
, domain
->name
));
310 /* user is the only member */
315 *gr_mem_len
= buf_len
;
317 DEBUG(10, ("fill_grent_mem_domain_users: num_mem = %u, len = %u, mem = %s\n",
318 (unsigned int)*num_gr_mem
,
319 (unsigned int)buf_len
, *num_gr_mem
? buf
: "NULL"));
324 /***********************************************************************
325 Add names to a list. Assumes a canonical version of the string
327 ***********************************************************************/
329 static int namecmp( const void *a
, const void *b
)
331 return StrCaseCmp( * (char * const *) a
, * (char * const *) b
);
334 static NTSTATUS
add_names_to_list( TALLOC_CTX
*ctx
,
335 char ***list
, uint32
*n_list
,
336 char **names
, uint32 n_names
)
338 char **new_list
= NULL
;
339 uint32 n_new_list
= 0;
342 if ( !names
|| (n_names
== 0) )
345 /* Alloc the maximum size we'll need */
347 if ( *list
== NULL
) {
348 if ( (new_list
= TALLOC_ARRAY( ctx
, char *, n_names
)) == NULL
)
349 return NT_STATUS_NO_MEMORY
;
350 n_new_list
= n_names
;
352 new_list
= TALLOC_REALLOC_ARRAY( ctx
, *list
, char *,
353 (*n_list
) + n_names
);
355 return NT_STATUS_NO_MEMORY
;
356 n_new_list
= (*n_list
) + n_names
;
361 for ( i
=*n_list
, j
=0; i
<n_new_list
; i
++, j
++ ) {
362 new_list
[i
] = talloc_strdup( new_list
, names
[j
] );
365 /* search for duplicates for sorting and looking for matching
368 qsort( new_list
, n_new_list
, sizeof(char*), QSORT_CAST namecmp
);
370 for ( i
=1; i
<n_new_list
; i
++ ) {
371 if ( strcmp( new_list
[i
-1], new_list
[i
] ) == 0 ) {
372 memmove( &new_list
[i
-1], &new_list
[i
],
373 sizeof(char*)*(n_new_list
-i
) );
379 *n_list
= n_new_list
;
384 /***********************************************************************
385 ***********************************************************************/
387 static NTSTATUS
expand_groups( TALLOC_CTX
*ctx
,
388 struct winbindd_domain
*d
,
389 DOM_SID
*glist
, uint32 n_glist
,
390 DOM_SID
**new_glist
, uint32
*n_new_glist
,
391 char ***members
, uint32
*n_members
)
394 NTSTATUS status
= NT_STATUS_OK
;
395 uint32 num_names
= 0;
396 uint32
*name_types
= NULL
;
398 DOM_SID
*sid_mem
= NULL
;
399 TALLOC_CTX
*tmp_ctx
= NULL
;
400 DOM_SID
*new_groups
= NULL
;
401 size_t new_groups_size
= 0;
408 for ( i
=0; i
<n_glist
; i
++ ) {
409 tmp_ctx
= talloc_new( ctx
);
411 /* Lookup the group membership */
413 status
= d
->methods
->lookup_groupmem(d
, tmp_ctx
,
414 &glist
[i
], &num_names
,
417 if ( !NT_STATUS_IS_OK(status
) )
420 /* Separate users and groups into two lists */
422 for ( j
=0; j
<num_names
; j
++ ) {
425 if ( name_types
[j
] == SID_NAME_USER
||
426 name_types
[j
] == SID_NAME_COMPUTER
)
428 status
= add_names_to_list( ctx
, members
,
431 if ( !NT_STATUS_IS_OK(status
) )
438 if ( name_types
[j
] == SID_NAME_DOM_GRP
||
439 name_types
[j
] == SID_NAME_ALIAS
)
441 status
= add_sid_to_array_unique(ctx
,
445 if (NT_STATUS_IS_OK(status
)) {
453 TALLOC_FREE( tmp_ctx
);
456 *new_glist
= new_groups
;
457 *n_new_glist
= (uint32
)new_groups_size
;
460 TALLOC_FREE( tmp_ctx
);
465 /***********************************************************************
466 Fill in the group membership field of a NT group given by group_sid
467 ***********************************************************************/
469 static bool fill_grent_mem(struct winbindd_domain
*domain
,
470 struct winbindd_cli_state
*state
,
472 enum lsa_SidType group_name_type
,
473 size_t *num_gr_mem
, char **gr_mem
, size_t *gr_mem_len
)
475 uint32 num_names
= 0;
476 unsigned int buf_len
= 0, buf_ndx
= 0, i
;
477 char **names
= NULL
, *buf
= NULL
;
481 DOM_SID
*glist
= NULL
;
482 DOM_SID
*new_glist
= NULL
;
483 uint32 n_glist
, n_new_glist
;
484 int max_depth
= lp_winbind_expand_groups();
486 if (!(mem_ctx
= talloc_init("fill_grent_mem(%s)", domain
->name
)))
489 DEBUG(10, ("group SID %s\n", sid_string_dbg(group_sid
)));
491 /* Initialize with no members */
495 /* HACK ALERT!! This whole routine does not cope with group members
496 * from more than one domain, ie aliases. Thus we have to work it out
497 * ourselves in a special routine. */
499 if (domain
->internal
) {
500 result
= fill_passdb_alias_grmem(domain
, group_sid
,
506 /* Verify name type */
508 if ( !((group_name_type
==SID_NAME_DOM_GRP
) ||
509 ((group_name_type
==SID_NAME_ALIAS
) && domain
->primary
)) )
511 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
512 sid_string_dbg(group_sid
),
513 domain
->name
, group_name_type
));
517 /* OPTIMIZATION / HACK. See comment in
518 fill_grent_mem_domusers() */
520 sid_peek_rid( group_sid
, &group_rid
);
521 if (!lp_winbind_enum_users() && group_rid
== DOMAIN_GROUP_RID_USERS
) {
522 result
= fill_grent_mem_domusers( mem_ctx
, domain
, state
,
523 group_sid
, group_name_type
,
529 /* Real work goes here. Create a list of group names to
530 expand startign with the initial one. Pass that to
531 expand_groups() which returns a list of more group names
532 to expand. Do this up to the max search depth. */
534 if ( (glist
= TALLOC_ARRAY(mem_ctx
, DOM_SID
, 1 )) == NULL
) {
536 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
539 sid_copy( &glist
[0], group_sid
);
542 for ( i
=0; i
<max_depth
&& glist
; i
++ ) {
543 uint32 n_members
= 0;
544 char **members
= NULL
;
547 nt_status
= expand_groups( mem_ctx
, domain
,
549 &new_glist
, &n_new_glist
,
550 &members
, &n_members
);
551 if ( !NT_STATUS_IS_OK(nt_status
) ) {
556 /* Add new group members to list */
558 nt_status
= add_names_to_list( mem_ctx
, &names
, &num_names
,
559 members
, n_members
);
560 if ( !NT_STATUS_IS_OK(nt_status
) ) {
565 TALLOC_FREE( members
);
567 /* If we have no more groups to expand, break out
576 n_glist
= n_new_glist
;
578 TALLOC_FREE( glist
);
580 DEBUG(10, ("looked up %d names\n", num_names
));
583 /* Add members to list */
585 for (i
= 0; i
< num_names
; i
++) {
588 DEBUG(10, ("processing name %s\n", names
[i
]));
590 len
= strlen(names
[i
]);
592 /* Add to list or calculate buffer length */
595 buf_len
+= len
+ 1; /* List is comma separated */
597 DEBUG(10, ("buf_len + %d = %d\n", len
+ 1, buf_len
));
599 DEBUG(10, ("appending %s at ndx %d\n", names
[i
], buf_ndx
));
600 safe_strcpy(&buf
[buf_ndx
], names
[i
], len
);
607 /* Allocate buffer */
609 if (!buf
&& buf_len
!= 0) {
610 if (!(buf
= (char *)SMB_MALLOC(buf_len
))) {
611 DEBUG(1, ("out of memory\n"));
615 memset(buf
, 0, buf_len
);
621 if (buf
&& buf_ndx
> 0) {
622 buf
[buf_ndx
- 1] = '\0';
626 *gr_mem_len
= buf_len
;
628 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem
,
629 (unsigned int)buf_len
, *num_gr_mem
? buf
: "NULL"));
634 talloc_destroy(mem_ctx
);
636 DEBUG(10, ("fill_grent_mem returning %d\n", result
));
641 static void winbindd_getgrsid( struct winbindd_cli_state
*state
, DOM_SID group_sid
);
643 static void getgrnam_recv( void *private_data
, bool success
, const DOM_SID
*sid
,
644 enum lsa_SidType type
)
646 struct winbindd_cli_state
*state
= (struct winbindd_cli_state
*)private_data
;
649 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
650 request_error(state
);
654 if ( (type
!= SID_NAME_DOM_GRP
) && (type
!= SID_NAME_ALIAS
) ) {
655 DEBUG(5,("getgrnam_recv: not a group!\n"));
656 request_error(state
);
660 winbindd_getgrsid( state
, *sid
);
664 /* Return a group structure from a group name */
666 void winbindd_getgrnam(struct winbindd_cli_state
*state
)
668 struct winbindd_domain
*domain
;
669 fstring name_domain
, name_group
;
672 /* Ensure null termination */
673 state
->request
.data
.groupname
[sizeof(state
->request
.data
.groupname
)-1]='\0';
675 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state
->pid
,
676 state
->request
.data
.groupname
));
678 /* Parse domain and groupname */
680 memset(name_group
, 0, sizeof(fstring
));
682 tmp
= state
->request
.data
.groupname
;
684 name_domain
[0] = '\0';
685 name_group
[0] = '\0';
687 parse_domain_user(tmp
, name_domain
, name_group
);
689 /* if no domain or our local domain and no local tdb group, default to
690 * our local domain for aliases */
692 if ( !*name_domain
|| strequal(name_domain
, get_global_sam_name()) ) {
693 fstrcpy(name_domain
, get_global_sam_name());
696 /* Get info for the domain */
698 if ((domain
= find_domain_from_name(name_domain
)) == NULL
) {
699 DEBUG(3, ("could not get domain sid for domain %s\n",
701 request_error(state
);
704 /* should we deal with users for our domain? */
706 if ( lp_winbind_trusted_domains_only() && domain
->primary
) {
707 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
708 "getgrnam() for %s\\%s.\n", name_domain
, name_group
));
709 request_error(state
);
713 /* Get rid and name type from name */
715 ws_name_replace( name_group
, WB_REPLACE_CHAR
);
717 winbindd_lookupname_async( state
->mem_ctx
, domain
->name
, name_group
,
718 getgrnam_recv
, WINBINDD_GETGRNAM
, state
);
721 struct getgrsid_state
{
722 struct winbindd_cli_state
*state
;
723 struct winbindd_domain
*domain
;
725 enum lsa_SidType group_type
;
730 static void getgrsid_sid2gid_recv(void *private_data
, bool success
, gid_t gid
)
732 struct getgrsid_state
*s
=
733 (struct getgrsid_state
*)private_data
;
734 struct winbindd_domain
*domain
;
738 fstring dom_name
, group_name
;
741 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
742 request_error(s
->state
);
748 if ( !parse_domain_user( s
->group_name
, dom_name
, group_name
) ) {
749 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
750 request_error(s
->state
);
755 /* Fill in group structure */
757 if ( (domain
= find_domain_from_name_noinit(dom_name
)) == NULL
) {
758 DEBUG(1,("Can't find domain from name (%s)\n", dom_name
));
759 request_error(s
->state
);
763 if (!fill_grent(&s
->state
->response
.data
.gr
, dom_name
, group_name
, gid
) ||
764 !fill_grent_mem(domain
, s
->state
, &s
->group_sid
, s
->group_type
,
765 &num_gr_mem
, &gr_mem
, &gr_mem_len
))
767 request_error(s
->state
);
771 s
->state
->response
.data
.gr
.num_gr_mem
= (uint32
)num_gr_mem
;
773 /* Group membership lives at start of extra data */
775 s
->state
->response
.data
.gr
.gr_mem_ofs
= 0;
777 s
->state
->response
.length
+= gr_mem_len
;
778 s
->state
->response
.extra_data
.data
= gr_mem
;
780 request_ok(s
->state
);
783 static void getgrsid_lookupsid_recv( void *private_data
, bool success
,
784 const char *dom_name
, const char *name
,
785 enum lsa_SidType name_type
)
787 struct getgrsid_state
*s
= (struct getgrsid_state
*)private_data
;
790 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
791 request_error(s
->state
);
795 /* either it's a domain group, a domain local group, or a
796 local group in an internal domain */
798 if ( !( (name_type
==SID_NAME_DOM_GRP
) ||
799 ((name_type
==SID_NAME_ALIAS
) &&
800 (s
->domain
->primary
|| s
->domain
->internal
)) ) )
802 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n",
803 dom_name
, name
, name_type
));
804 request_error(s
->state
);
808 if ( (s
->group_name
= talloc_asprintf( s
->state
->mem_ctx
,
810 dom_name
, name
)) == NULL
)
812 DEBUG(1, ("getgrsid_lookupsid_recv: talloc_asprintf() Failed!\n"));
813 request_error(s
->state
);
817 s
->group_type
= name_type
;
819 winbindd_sid2gid_async(s
->state
->mem_ctx
, &s
->group_sid
,
820 getgrsid_sid2gid_recv
, s
);
823 static void winbindd_getgrsid( struct winbindd_cli_state
*state
, const DOM_SID group_sid
)
825 struct getgrsid_state
*s
;
827 if ( (s
= TALLOC_ZERO_P(state
->mem_ctx
, struct getgrsid_state
)) == NULL
) {
828 DEBUG(0, ("talloc failed\n"));
829 request_error(state
);
835 if ( (s
->domain
= find_domain_from_sid_noinit(&group_sid
)) == NULL
) {
836 DEBUG(3, ("Could not find domain for sid %s\n",
837 sid_string_dbg(&group_sid
)));
838 request_error(state
);
842 sid_copy(&s
->group_sid
, &group_sid
);
844 winbindd_lookupsid_async( s
->state
->mem_ctx
, &group_sid
,
845 getgrsid_lookupsid_recv
, s
);
849 static void getgrgid_recv(void *private_data
, bool success
, const char *sid
)
851 struct winbindd_cli_state
*state
= talloc_get_type_abort(private_data
, struct winbindd_cli_state
);
852 enum lsa_SidType name_type
;
856 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
857 (unsigned long)(state
->request
.data
.gid
), sid
));
859 string_to_sid(&group_sid
, sid
);
860 winbindd_getgrsid(state
, group_sid
);
864 /* Ok, this might be "ours", i.e. an alias */
865 if (pdb_gid_to_sid(state
->request
.data
.gid
, &group_sid
) &&
866 lookup_sid(state
->mem_ctx
, &group_sid
, NULL
, NULL
, &name_type
) &&
867 (name_type
== SID_NAME_ALIAS
)) {
868 /* Hey, got an alias */
869 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
870 (unsigned long)(state
->request
.data
.gid
), sid
));
871 winbindd_getgrsid(state
, group_sid
);
875 DEBUG(1, ("could not convert gid %lu to sid\n",
876 (unsigned long)state
->request
.data
.gid
));
877 request_error(state
);
880 /* Return a group structure from a gid number */
881 void winbindd_getgrgid(struct winbindd_cli_state
*state
)
883 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state
->pid
,
884 (unsigned long)state
->request
.data
.gid
));
886 /* always use the async interface */
887 winbindd_gid2sid_async(state
->mem_ctx
, state
->request
.data
.gid
, getgrgid_recv
, state
);
891 * set/get/endgrent functions
894 /* "Rewind" file pointer for group database enumeration */
896 static bool winbindd_setgrent_internal(struct winbindd_cli_state
*state
)
898 struct winbindd_domain
*domain
;
900 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state
->pid
));
902 /* Check user has enabled this */
904 if (!lp_winbind_enum_groups()) {
908 /* Free old static data if it exists */
910 if (state
->getgrent_state
!= NULL
) {
911 free_getent_state(state
->getgrent_state
);
912 state
->getgrent_state
= NULL
;
915 /* Create sam pipes for each domain we know about */
917 for (domain
= domain_list(); domain
!= NULL
; domain
= domain
->next
) {
918 struct getent_state
*domain_state
;
920 /* Create a state record for this domain */
922 /* don't add our domaina if we are a PDC or if we
923 are a member of a Samba domain */
925 if ( lp_winbind_trusted_domains_only() && domain
->primary
)
931 if ((domain_state
= SMB_MALLOC_P(struct getent_state
)) == NULL
) {
932 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
936 ZERO_STRUCTP(domain_state
);
938 fstrcpy(domain_state
->domain_name
, domain
->name
);
940 /* Add to list of open domains */
942 DLIST_ADD(state
->getgrent_state
, domain_state
);
945 state
->getgrent_initialized
= True
;
949 void winbindd_setgrent(struct winbindd_cli_state
*state
)
951 if (winbindd_setgrent_internal(state
)) {
954 request_error(state
);
958 /* Close file pointer to ntdom group database */
960 void winbindd_endgrent(struct winbindd_cli_state
*state
)
962 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state
->pid
));
964 free_getent_state(state
->getgrent_state
);
965 state
->getgrent_initialized
= False
;
966 state
->getgrent_state
= NULL
;
970 /* Get the list of domain groups and domain aliases for a domain. We fill in
971 the sam_entries and num_sam_entries fields with domain group information.
972 The dispinfo_ndx field is incremented to the index of the next group to
973 fetch. Return True if some groups were returned, False otherwise. */
975 static bool get_sam_group_entries(struct getent_state
*ent
)
979 struct acct_info
*name_list
= NULL
;
982 struct acct_info
*sam_grp_entries
= NULL
;
983 struct winbindd_domain
*domain
;
985 if (ent
->got_sam_entries
)
988 if (!(mem_ctx
= talloc_init("get_sam_group_entries(%s)",
989 ent
->domain_name
))) {
990 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
994 /* Free any existing group info */
996 SAFE_FREE(ent
->sam_entries
);
997 ent
->num_sam_entries
= 0;
998 ent
->got_sam_entries
= True
;
1000 /* Enumerate domain groups */
1004 if (!(domain
= find_domain_from_name(ent
->domain_name
))) {
1005 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent
->domain_name
));
1009 /* always get the domain global groups */
1011 status
= domain
->methods
->enum_dom_groups(domain
, mem_ctx
, &num_entries
, &sam_grp_entries
);
1013 if (!NT_STATUS_IS_OK(status
)) {
1014 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status
)));
1019 /* Copy entries into return buffer */
1022 if ( !(name_list
= SMB_MALLOC_ARRAY(struct acct_info
, num_entries
)) ) {
1023 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
1028 memcpy( name_list
, sam_grp_entries
, num_entries
* sizeof(struct acct_info
) );
1031 ent
->num_sam_entries
= num_entries
;
1033 /* get the domain local groups if we are a member of a native win2k domain
1034 and are not using LDAP to get the groups */
1036 if ( ( lp_security() != SEC_ADS
&& domain
->native_mode
1037 && domain
->primary
) || domain
->internal
)
1039 DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n",
1040 domain
->native_mode
? "Native Mode 2k":"BUILTIN or local"));
1042 status
= domain
->methods
->enum_local_groups(domain
, mem_ctx
, &num_entries
, &sam_grp_entries
);
1044 if ( !NT_STATUS_IS_OK(status
) ) {
1045 DEBUG(3,("get_sam_group_entries: "
1046 "Failed to enumerate "
1047 "domain local groups with error %s!\n",
1048 nt_errstr(status
)));
1052 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries
));
1054 /* Copy entries into return buffer */
1056 if ( num_entries
) {
1057 if ( !(name_list
= SMB_REALLOC_ARRAY( name_list
, struct acct_info
, ent
->num_sam_entries
+num_entries
)) )
1059 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
1065 memcpy( &name_list
[ent
->num_sam_entries
], sam_grp_entries
,
1066 num_entries
* sizeof(struct acct_info
) );
1069 ent
->num_sam_entries
+= num_entries
;
1073 /* Fill in remaining fields */
1075 ent
->sam_entries
= name_list
;
1076 ent
->sam_entry_index
= 0;
1078 result
= (ent
->num_sam_entries
> 0);
1081 talloc_destroy(mem_ctx
);
1086 /* Fetch next group entry from ntdom database */
1088 #define MAX_GETGRENT_GROUPS 500
1090 void winbindd_getgrent(struct winbindd_cli_state
*state
)
1092 struct getent_state
*ent
;
1093 struct winbindd_gr
*group_list
= NULL
;
1094 int num_groups
, group_list_ndx
, gr_mem_list_len
= 0;
1095 char *gr_mem_list
= NULL
;
1097 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state
->pid
));
1099 /* Check user has enabled this */
1101 if (!lp_winbind_enum_groups()) {
1102 request_error(state
);
1106 num_groups
= MIN(MAX_GETGRENT_GROUPS
, state
->request
.data
.num_entries
);
1108 if (num_groups
== 0) {
1109 request_error(state
);
1113 if ((state
->response
.extra_data
.data
= SMB_MALLOC_ARRAY(struct winbindd_gr
, num_groups
)) == NULL
) {
1114 request_error(state
);
1118 memset(state
->response
.extra_data
.data
, '\0',
1119 num_groups
* sizeof(struct winbindd_gr
) );
1121 state
->response
.data
.num_entries
= 0;
1123 group_list
= (struct winbindd_gr
*)state
->response
.extra_data
.data
;
1125 if (!state
->getgrent_initialized
)
1126 winbindd_setgrent_internal(state
);
1128 if (!(ent
= state
->getgrent_state
)) {
1129 request_error(state
);
1133 /* Start sending back groups */
1135 for (group_list_ndx
= 0; group_list_ndx
< num_groups
; ) {
1136 struct acct_info
*name_list
= NULL
;
1137 fstring domain_group_name
;
1143 struct winbindd_domain
*domain
;
1145 /* Do we need to fetch another chunk of groups? */
1149 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1150 ent
->sam_entry_index
, ent
->num_sam_entries
));
1152 if (ent
->num_sam_entries
== ent
->sam_entry_index
) {
1154 while(ent
&& !get_sam_group_entries(ent
)) {
1155 struct getent_state
*next_ent
;
1157 DEBUG(10, ("freeing state info for domain %s\n", ent
->domain_name
));
1159 /* Free state information for this domain */
1161 SAFE_FREE(ent
->sam_entries
);
1163 next_ent
= ent
->next
;
1164 DLIST_REMOVE(state
->getgrent_state
, ent
);
1170 /* No more domains */
1176 name_list
= (struct acct_info
*)ent
->sam_entries
;
1179 find_domain_from_name(ent
->domain_name
))) {
1180 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent
->domain_name
));
1185 /* Lookup group info */
1187 sid_copy(&group_sid
, &domain
->sid
);
1188 sid_append_rid(&group_sid
, name_list
[ent
->sam_entry_index
].rid
);
1190 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid
, &group_gid
))) {
1192 enum lsa_SidType type
;
1194 DEBUG(10, ("SID %s not in idmap\n",
1195 sid_string_dbg(&group_sid
)));
1197 if (!pdb_sid_to_id(&group_sid
, &id
, &type
)) {
1198 DEBUG(1, ("could not look up gid for group "
1200 name_list
[ent
->sam_entry_index
].acct_name
));
1201 ent
->sam_entry_index
++;
1205 if ((type
!= SID_NAME_DOM_GRP
) &&
1206 (type
!= SID_NAME_ALIAS
) &&
1207 (type
!= SID_NAME_WKN_GRP
)) {
1208 DEBUG(1, ("Group %s is a %s, not a group\n",
1209 sid_type_lookup(type
),
1210 name_list
[ent
->sam_entry_index
].acct_name
));
1211 ent
->sam_entry_index
++;
1217 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid
,
1218 (unsigned long)name_list
[ent
->sam_entry_index
].rid
));
1220 /* Fill in group entry */
1222 fill_domain_username(domain_group_name
, ent
->domain_name
,
1223 name_list
[ent
->sam_entry_index
].acct_name
, True
);
1225 result
= fill_grent(&group_list
[group_list_ndx
],
1227 name_list
[ent
->sam_entry_index
].acct_name
,
1230 /* Fill in group membership entry */
1233 size_t num_gr_mem
= 0;
1235 group_list
[group_list_ndx
].num_gr_mem
= 0;
1239 /* Get group membership */
1240 if (state
->request
.cmd
== WINBINDD_GETGRLST
) {
1243 sid_copy(&member_sid
, &domain
->sid
);
1244 sid_append_rid(&member_sid
, name_list
[ent
->sam_entry_index
].rid
);
1245 result
= fill_grent_mem(
1251 &gr_mem
, &gr_mem_len
);
1253 group_list
[group_list_ndx
].num_gr_mem
= (uint32
)num_gr_mem
;
1258 /* Append to group membership list */
1259 gr_mem_list
= (char *)SMB_REALLOC(
1260 gr_mem_list
, gr_mem_list_len
+ gr_mem_len
);
1262 if (!gr_mem_list
&& (group_list
[group_list_ndx
].num_gr_mem
!= 0)) {
1263 DEBUG(0, ("out of memory\n"));
1264 gr_mem_list_len
= 0;
1268 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1269 gr_mem_list_len
, (unsigned int)gr_mem_len
));
1271 memcpy(&gr_mem_list
[gr_mem_list_len
], gr_mem
,
1276 group_list
[group_list_ndx
].gr_mem_ofs
=
1279 gr_mem_list_len
+= gr_mem_len
;
1282 ent
->sam_entry_index
++;
1284 /* Add group to return list */
1288 DEBUG(10, ("adding group num_entries = %d\n",
1289 state
->response
.data
.num_entries
));
1292 state
->response
.data
.num_entries
++;
1294 state
->response
.length
+=
1295 sizeof(struct winbindd_gr
);
1298 DEBUG(0, ("could not lookup domain group %s\n",
1299 domain_group_name
));
1303 /* Copy the list of group memberships to the end of the extra data */
1305 if (group_list_ndx
== 0)
1308 state
->response
.extra_data
.data
= SMB_REALLOC(
1309 state
->response
.extra_data
.data
,
1310 group_list_ndx
* sizeof(struct winbindd_gr
) + gr_mem_list_len
);
1312 if (!state
->response
.extra_data
.data
) {
1313 DEBUG(0, ("out of memory\n"));
1315 SAFE_FREE(gr_mem_list
);
1316 request_error(state
);
1320 memcpy(&((char *)state
->response
.extra_data
.data
)
1321 [group_list_ndx
* sizeof(struct winbindd_gr
)],
1322 gr_mem_list
, gr_mem_list_len
);
1324 state
->response
.length
+= gr_mem_list_len
;
1326 DEBUG(10, ("returning %d groups, length = %d\n",
1327 group_list_ndx
, gr_mem_list_len
));
1329 /* Out of domains */
1333 SAFE_FREE(gr_mem_list
);
1335 if (group_list_ndx
> 0)
1338 request_error(state
);
1341 /* List domain groups without mapping to unix ids */
1343 void winbindd_list_groups(struct winbindd_cli_state
*state
)
1345 uint32 total_entries
= 0;
1346 struct winbindd_domain
*domain
;
1347 const char *which_domain
;
1348 char *extra_data
= NULL
;
1349 unsigned int extra_data_len
= 0, i
;
1351 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state
->pid
));
1353 /* Ensure null termination */
1354 state
->request
.domain_name
[sizeof(state
->request
.domain_name
)-1]='\0';
1355 which_domain
= state
->request
.domain_name
;
1357 /* Enumerate over trusted domains */
1359 for (domain
= domain_list(); domain
; domain
= domain
->next
) {
1360 struct getent_state groups
;
1362 /* if we have a domain name restricting the request and this
1363 one in the list doesn't match, then just bypass the remainder
1366 if ( *which_domain
&& !strequal(which_domain
, domain
->name
) )
1369 ZERO_STRUCT(groups
);
1371 /* Get list of sam groups */
1373 fstrcpy(groups
.domain_name
, domain
->name
);
1375 get_sam_group_entries(&groups
);
1377 if (groups
.num_sam_entries
== 0) {
1378 /* this domain is empty or in an error state */
1382 /* keep track the of the total number of groups seen so
1383 far over all domains */
1384 total_entries
+= groups
.num_sam_entries
;
1386 /* Allocate some memory for extra data. Note that we limit
1387 account names to sizeof(fstring) = 128 characters. */
1388 extra_data
= (char *)SMB_REALLOC(
1389 extra_data
, sizeof(fstring
) * total_entries
);
1392 DEBUG(0,("failed to enlarge buffer!\n"));
1393 request_error(state
);
1397 /* Pack group list into extra data fields */
1398 for (i
= 0; i
< groups
.num_sam_entries
; i
++) {
1399 char *group_name
= ((struct acct_info
*)
1400 groups
.sam_entries
)[i
].acct_name
;
1403 fill_domain_username(name
, domain
->name
, group_name
, True
);
1404 /* Append to extra data */
1405 memcpy(&extra_data
[extra_data_len
], name
,
1407 extra_data_len
+= strlen(name
);
1408 extra_data
[extra_data_len
++] = ',';
1411 SAFE_FREE(groups
.sam_entries
);
1414 /* Assign extra_data fields in response structure */
1416 extra_data
[extra_data_len
- 1] = '\0';
1417 state
->response
.extra_data
.data
= extra_data
;
1418 state
->response
.length
+= extra_data_len
;
1421 /* No domains may have responded but that's still OK so don't
1427 /* Get user supplementary groups. This is much quicker than trying to
1428 invert the groups database. We merge the groups from the gids and
1429 other_sids info3 fields as trusted domain, universal group
1430 memberships, and nested groups (win2k native mode only) are not
1431 returned by the getgroups RPC call but are present in the info3. */
1433 struct getgroups_state
{
1434 struct winbindd_cli_state
*state
;
1435 struct winbindd_domain
*domain
;
1440 const DOM_SID
*token_sids
;
1441 size_t i
, num_token_sids
;
1444 size_t num_token_gids
;
1447 static void getgroups_usersid_recv(void *private_data
, bool success
,
1448 const DOM_SID
*sid
, enum lsa_SidType type
);
1449 static void getgroups_tokensids_recv(void *private_data
, bool success
,
1450 DOM_SID
*token_sids
, size_t num_token_sids
);
1451 static void getgroups_sid2gid_recv(void *private_data
, bool success
, gid_t gid
);
1453 void winbindd_getgroups(struct winbindd_cli_state
*state
)
1455 struct getgroups_state
*s
;
1457 /* Ensure null termination */
1458 state
->request
.data
.username
1459 [sizeof(state
->request
.data
.username
)-1]='\0';
1461 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state
->pid
,
1462 state
->request
.data
.username
));
1464 /* Parse domain and username */
1466 s
= TALLOC_P(state
->mem_ctx
, struct getgroups_state
);
1468 DEBUG(0, ("talloc failed\n"));
1469 request_error(state
);
1475 ws_name_return( state
->request
.data
.username
, WB_REPLACE_CHAR
);
1477 if (!parse_domain_user_talloc(state
->mem_ctx
,
1478 state
->request
.data
.username
,
1479 &s
->domname
, &s
->username
)) {
1480 DEBUG(5, ("Could not parse domain user: %s\n",
1481 state
->request
.data
.username
));
1483 /* error out if we do not have nested group support */
1485 if ( !lp_winbind_nested_groups() ) {
1486 request_error(state
);
1490 s
->domname
= talloc_strdup( state
->mem_ctx
, get_global_sam_name() );
1491 s
->username
= talloc_strdup( state
->mem_ctx
, state
->request
.data
.username
);
1494 /* Get info for the domain (either by short domain name or
1495 DNS name in the case of a UPN) */
1497 s
->domain
= find_domain_from_name_noinit(s
->domname
);
1499 char *p
= strchr(s
->username
, '@');
1502 s
->domain
= find_domain_from_name_noinit(p
+1);
1507 if (s
->domain
== NULL
) {
1508 DEBUG(7, ("could not find domain entry for domain %s\n",
1510 request_error(state
);
1514 if ( s
->domain
->primary
&& lp_winbind_trusted_domains_only()) {
1515 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1516 "getgroups() for %s\\%s.\n", s
->domname
,
1518 request_error(state
);
1522 /* Get rid and name type from name. The following costs 1 packet */
1524 winbindd_lookupname_async(state
->mem_ctx
, s
->domname
, s
->username
,
1525 getgroups_usersid_recv
, WINBINDD_GETGROUPS
, s
);
1528 static void getgroups_usersid_recv(void *private_data
, bool success
,
1529 const DOM_SID
*sid
, enum lsa_SidType type
)
1531 struct getgroups_state
*s
=
1532 (struct getgroups_state
*)private_data
;
1535 ((type
!= SID_NAME_USER
) && (type
!= SID_NAME_COMPUTER
))) {
1536 request_error(s
->state
);
1540 sid_copy(&s
->user_sid
, sid
);
1542 winbindd_gettoken_async(s
->state
->mem_ctx
, &s
->user_sid
,
1543 getgroups_tokensids_recv
, s
);
1546 static void getgroups_tokensids_recv(void *private_data
, bool success
,
1547 DOM_SID
*token_sids
, size_t num_token_sids
)
1549 struct getgroups_state
*s
=
1550 (struct getgroups_state
*)private_data
;
1552 /* We need at least the user sid and the primary group in the token,
1553 * otherwise it's an error */
1555 if ((!success
) || (num_token_sids
< 2)) {
1556 request_error(s
->state
);
1560 s
->token_sids
= token_sids
;
1561 s
->num_token_sids
= num_token_sids
;
1564 s
->token_gids
= NULL
;
1565 s
->num_token_gids
= 0;
1567 getgroups_sid2gid_recv(s
, False
, 0);
1570 static void getgroups_sid2gid_recv(void *private_data
, bool success
, gid_t gid
)
1572 struct getgroups_state
*s
=
1573 (struct getgroups_state
*)private_data
;
1576 if (!add_gid_to_array_unique(s
->state
->mem_ctx
, gid
,
1578 &s
->num_token_gids
)) {
1583 if (s
->i
< s
->num_token_sids
) {
1584 const DOM_SID
*sid
= &s
->token_sids
[s
->i
];
1587 if (sid_equal(sid
, &s
->user_sid
)) {
1588 getgroups_sid2gid_recv(s
, False
, 0);
1592 winbindd_sid2gid_async(s
->state
->mem_ctx
, sid
,
1593 getgroups_sid2gid_recv
, s
);
1597 s
->state
->response
.data
.num_entries
= s
->num_token_gids
;
1598 /* s->token_gids are talloced */
1599 s
->state
->response
.extra_data
.data
= smb_xmemdup(s
->token_gids
, s
->num_token_gids
* sizeof(gid_t
));
1600 s
->state
->response
.length
+= s
->num_token_gids
* sizeof(gid_t
);
1601 request_ok(s
->state
);
1604 /* Get user supplementary sids. This is equivalent to the
1605 winbindd_getgroups() function but it involves a SID->SIDs mapping
1606 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1607 idmap. This call is designed to be used with applications that need
1608 to do ACL evaluation themselves. Note that the cached info3 data is
1611 this function assumes that the SID that comes in is a user SID. If
1612 you pass in another type of SID then you may get unpredictable
1616 static void getusersids_recv(void *private_data
, bool success
, DOM_SID
*sids
,
1619 void winbindd_getusersids(struct winbindd_cli_state
*state
)
1623 /* Ensure null termination */
1624 state
->request
.data
.sid
[sizeof(state
->request
.data
.sid
)-1]='\0';
1626 user_sid
= TALLOC_P(state
->mem_ctx
, DOM_SID
);
1627 if (user_sid
== NULL
) {
1628 DEBUG(1, ("talloc failed\n"));
1629 request_error(state
);
1633 if (!string_to_sid(user_sid
, state
->request
.data
.sid
)) {
1634 DEBUG(1, ("Could not get convert sid %s from string\n",
1635 state
->request
.data
.sid
));
1636 request_error(state
);
1640 winbindd_gettoken_async(state
->mem_ctx
, user_sid
, getusersids_recv
,
1644 static void getusersids_recv(void *private_data
, bool success
, DOM_SID
*sids
,
1647 struct winbindd_cli_state
*state
=
1648 (struct winbindd_cli_state
*)private_data
;
1650 unsigned ofs
, ret_size
= 0;
1654 request_error(state
);
1658 /* work out the response size */
1659 for (i
= 0; i
< num_sids
; i
++) {
1661 sid_to_fstring(s
, &sids
[i
]);
1662 ret_size
+= strlen(s
) + 1;
1665 /* build the reply */
1666 ret
= (char *)SMB_MALLOC(ret_size
);
1668 DEBUG(0, ("malloc failed\n"));
1669 request_error(state
);
1673 for (i
= 0; i
< num_sids
; i
++) {
1675 sid_to_fstring(s
, &sids
[i
]);
1676 safe_strcpy(ret
+ ofs
, s
, ret_size
- ofs
- 1);
1677 ofs
+= strlen(ret
+ofs
) + 1;
1680 /* Send data back to client */
1681 state
->response
.data
.num_entries
= num_sids
;
1682 state
->response
.extra_data
.data
= ret
;
1683 state
->response
.length
+= ret_size
;
1687 void winbindd_getuserdomgroups(struct winbindd_cli_state
*state
)
1690 struct winbindd_domain
*domain
;
1692 /* Ensure null termination */
1693 state
->request
.data
.sid
[sizeof(state
->request
.data
.sid
)-1]='\0';
1695 if (!string_to_sid(&user_sid
, state
->request
.data
.sid
)) {
1696 DEBUG(1, ("Could not get convert sid %s from string\n",
1697 state
->request
.data
.sid
));
1698 request_error(state
);
1702 /* Get info for the domain */
1703 if ((domain
= find_domain_from_sid_noinit(&user_sid
)) == NULL
) {
1704 DEBUG(0,("could not find domain entry for sid %s\n",
1705 sid_string_dbg(&user_sid
)));
1706 request_error(state
);
1710 sendto_domain(state
, domain
);
1713 enum winbindd_result
winbindd_dual_getuserdomgroups(struct winbindd_domain
*domain
,
1714 struct winbindd_cli_state
*state
)
1724 /* Ensure null termination */
1725 state
->request
.data
.sid
[sizeof(state
->request
.data
.sid
)-1]='\0';
1727 if (!string_to_sid(&user_sid
, state
->request
.data
.sid
)) {
1728 DEBUG(1, ("Could not get convert sid %s from string\n",
1729 state
->request
.data
.sid
));
1730 return WINBINDD_ERROR
;
1733 status
= domain
->methods
->lookup_usergroups(domain
, state
->mem_ctx
,
1734 &user_sid
, &num_groups
,
1736 if (!NT_STATUS_IS_OK(status
))
1737 return WINBINDD_ERROR
;
1739 if (num_groups
== 0) {
1740 state
->response
.data
.num_entries
= 0;
1741 state
->response
.extra_data
.data
= NULL
;
1745 if (!print_sidlist(state
->mem_ctx
, groups
, num_groups
, &sidstring
, &len
)) {
1746 DEBUG(0, ("talloc failed\n"));
1747 return WINBINDD_ERROR
;
1750 state
->response
.extra_data
.data
= SMB_STRDUP(sidstring
);
1751 if (!state
->response
.extra_data
.data
) {
1752 return WINBINDD_ERROR
;
1754 state
->response
.length
+= len
+1;
1755 state
->response
.data
.num_entries
= num_groups
;