winbindd: speed up fill_grent_mem (i.e. winbindd_getgrent) a lot.
[Samba.git] / source / winbindd / winbindd_group.c
blob66d263402833c7b0c170f7693b1a8ef70c3f48fb
1 /*
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/>.
25 #include "includes.h"
26 #include "winbindd.h"
28 extern bool opt_nocache;
30 #undef DBGC_CLASS
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)
36 fstring name;
38 fill_domain_username(name, domain, user, True);
39 safe_strcat(name, ",", sizeof(name)-1);
40 string_append(pp_members, name);
41 *p_num_members += 1;
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)
50 DOM_SID dom_sid;
51 uint32 rid;
52 struct winbindd_domain *domain;
53 size_t i;
55 char *domain_name = NULL;
56 char *name = NULL;
57 enum lsa_SidType type;
59 uint32 num_names;
60 DOM_SID *sid_mem;
61 char **names;
62 uint32 *types;
64 NTSTATUS result;
66 TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
68 if (mem_ctx == NULL) {
69 DEBUG(1, ("talloc_init failed\n"));
70 return;
73 sid_copy(&dom_sid, sid);
74 sid_split_rid(&dom_sid, &rid);
76 domain = find_lookup_domain_from_sid(sid);
78 if (domain == NULL) {
79 DEBUG(3, ("Could not find domain for sid %s\n",
80 sid_string_dbg(sid)));
81 goto done;
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)));
90 goto done;
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);
97 goto done;
100 if (type != SID_NAME_DOM_GRP) {
101 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
102 name));
103 goto done;
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)));
113 goto done;
116 result = domain->methods->lookup_groupmem(domain, mem_ctx,
117 sid, &num_names,
118 &sid_mem, &names,
119 &types);
121 if (!NT_STATUS_IS_OK(result)) {
122 DEBUG(10, ("Could not lookup group members for %s: %s\n",
123 name, nt_errstr(result)));
124 goto done;
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));
134 continue;
137 add_member(domain->name, names[i], pp_members, p_num_members);
140 done:
141 talloc_destroy(mem_ctx);
142 return;
145 static bool fill_passdb_alias_grmem(struct winbindd_domain *domain,
146 DOM_SID *group_sid,
147 size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
149 DOM_SID *members;
150 size_t i, num_members;
152 *num_gr_mem = 0;
153 *gr_mem = NULL;
154 *gr_mem_len = 0;
156 if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members,
157 &num_members)))
158 return True;
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) {
167 size_t len;
169 /* We have at least one member, strip off the last "," */
170 len = strlen(*gr_mem);
171 (*gr_mem)[len-1] = '\0';
172 *gr_mem_len = len;
175 return True;
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);
194 return True;
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,
209 DOM_SID *group_sid,
210 enum lsa_SidType group_name_type,
211 size_t *num_gr_mem, char **gr_mem,
212 size_t *gr_mem_len)
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;
219 NTSTATUS status;
220 int i;
221 unsigned int buf_len = 0;
222 char *buf = NULL;
224 DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
225 domain->name ));
227 if (state) {
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,
250 mem_ctx,
251 pquerying_user_sid,
252 &num_groups,
253 &user_sids);
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),
258 domain->name,
259 nt_errstr(status)));
260 return False;
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. */
267 u_in_group = True;
268 break;
273 if (u_in_group) {
274 size_t len = 0;
275 char *domainname = NULL;
276 char *username = NULL;
277 fstring name;
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),
282 domain->name ));
284 status = domain->methods->sid_to_name(domain, mem_ctx,
285 pquerying_user_sid,
286 &domainname,
287 &username,
288 &type);
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),
293 domain->name,
294 nt_errstr(status)));
295 return False;
297 fill_domain_username(name, domain->name, username, True);
298 len = strlen(name);
299 buf_len = len + 1;
300 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
301 DEBUG(1, ("out of memory\n"));
302 return False;
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 */
311 *num_gr_mem = 1;
314 *gr_mem = buf;
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"));
321 return True;
324 /***********************************************************************
325 Add names to a list. Assumes a canonical version of the string
326 in DOMAIN\user
327 ***********************************************************************/
329 static int namecmp( const void *a, const void *b )
331 return StrCaseCmp( * (char * const *) a, * (char * const *) b);
334 static void sort_unique_list(char ***list, uint32 *n_list)
336 uint32_t i;
338 /* search for duplicates for sorting and looking for matching
339 neighbors */
341 qsort(*list, *n_list, sizeof(char*), QSORT_CAST namecmp);
343 for (i=1; i < *n_list; i++) {
344 if (strcmp((*list)[i-1], (*list)[i]) == 0) {
345 memmove(&((*list)[i-1]), &((*list)[i]),
346 sizeof(char*)*((*n_list)-i));
347 (*n_list)--;
352 static NTSTATUS add_names_to_list( TALLOC_CTX *ctx,
353 char ***list, uint32 *n_list,
354 char **names, uint32 n_names )
356 char **new_list = NULL;
357 uint32 n_new_list = 0;
358 int i, j;
360 if ( !names || (n_names == 0) )
361 return NT_STATUS_OK;
363 /* Alloc the maximum size we'll need */
365 if ( *list == NULL ) {
366 if ( (new_list = TALLOC_ARRAY( ctx, char *, n_names )) == NULL )
367 return NT_STATUS_NO_MEMORY;
368 n_new_list = n_names;
369 } else {
370 new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *,
371 (*n_list) + n_names );
372 if ( !new_list )
373 return NT_STATUS_NO_MEMORY;
374 n_new_list = (*n_list) + n_names;
377 /* Add all names */
379 for ( i=*n_list, j=0; i<n_new_list; i++, j++ ) {
380 new_list[i] = talloc_strdup( new_list, names[j] );
383 *list = new_list;
384 *n_list = n_new_list;
386 return NT_STATUS_OK;
389 /***********************************************************************
390 ***********************************************************************/
392 static NTSTATUS expand_groups( TALLOC_CTX *ctx,
393 struct winbindd_domain *d,
394 DOM_SID *glist, uint32 n_glist,
395 DOM_SID **new_glist, uint32 *n_new_glist,
396 char ***members, uint32 *n_members )
398 int i, j;
399 NTSTATUS status = NT_STATUS_OK;
400 uint32 num_names = 0;
401 uint32 *name_types = NULL;
402 char **names = NULL;
403 DOM_SID *sid_mem = NULL;
404 TALLOC_CTX *tmp_ctx = NULL;
405 DOM_SID *new_groups = NULL;
406 size_t new_groups_size = 0;
408 *members = NULL;
409 *n_members = 0;
410 *new_glist = NULL;
411 *n_new_glist = 0;
413 for ( i=0; i<n_glist; i++ ) {
414 tmp_ctx = talloc_new( ctx );
416 /* Lookup the group membership */
418 status = d->methods->lookup_groupmem(d, tmp_ctx,
419 &glist[i], &num_names,
420 &sid_mem, &names,
421 &name_types);
422 if ( !NT_STATUS_IS_OK(status) )
423 goto out;
425 /* Separate users and groups into two lists */
427 for ( j=0; j<num_names; j++ ) {
429 /* Users */
430 if ( name_types[j] == SID_NAME_USER ||
431 name_types[j] == SID_NAME_COMPUTER )
433 status = add_names_to_list( ctx, members,
434 n_members,
435 names+j, 1 );
436 if ( !NT_STATUS_IS_OK(status) )
437 goto out;
439 continue;
442 /* Groups */
443 if ( name_types[j] == SID_NAME_DOM_GRP ||
444 name_types[j] == SID_NAME_ALIAS )
446 status = add_sid_to_array_unique(ctx,
447 &sid_mem[j],
448 &new_groups,
449 &new_groups_size);
450 if (!NT_STATUS_IS_OK(status)) {
451 goto out;
454 continue;
458 TALLOC_FREE( tmp_ctx );
461 *new_glist = new_groups;
462 *n_new_glist = (uint32)new_groups_size;
464 out:
465 TALLOC_FREE( tmp_ctx );
467 return status;
470 /***********************************************************************
471 Fill in the group membership field of a NT group given by group_sid
472 ***********************************************************************/
474 static bool fill_grent_mem(struct winbindd_domain *domain,
475 struct winbindd_cli_state *state,
476 DOM_SID *group_sid,
477 enum lsa_SidType group_name_type,
478 size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
480 uint32 num_names = 0;
481 unsigned int buf_len = 0, buf_ndx = 0, i;
482 char **names = NULL, *buf = NULL;
483 bool result = False;
484 TALLOC_CTX *mem_ctx;
485 uint32 group_rid;
486 DOM_SID *glist = NULL;
487 DOM_SID *new_glist = NULL;
488 uint32 n_glist, n_new_glist;
489 int max_depth = lp_winbind_expand_groups();
491 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
492 return False;
494 DEBUG(10, ("group SID %s\n", sid_string_dbg(group_sid)));
496 /* Initialize with no members */
498 *num_gr_mem = 0;
500 /* HACK ALERT!! This whole routine does not cope with group members
501 * from more than one domain, ie aliases. Thus we have to work it out
502 * ourselves in a special routine. */
504 if (domain->internal) {
505 result = fill_passdb_alias_grmem(domain, group_sid,
506 num_gr_mem,
507 gr_mem, gr_mem_len);
508 goto done;
511 /* Verify name type */
513 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
514 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
516 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
517 sid_string_dbg(group_sid),
518 domain->name, group_name_type));
519 goto done;
522 /* OPTIMIZATION / HACK. See comment in
523 fill_grent_mem_domusers() */
525 sid_peek_rid( group_sid, &group_rid );
526 if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
527 result = fill_grent_mem_domusers( mem_ctx, domain, state,
528 group_sid, group_name_type,
529 num_gr_mem, gr_mem,
530 gr_mem_len );
531 goto done;
534 /* Real work goes here. Create a list of group names to
535 expand startign with the initial one. Pass that to
536 expand_groups() which returns a list of more group names
537 to expand. Do this up to the max search depth. */
539 if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) {
540 result = False;
541 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
542 goto done;
544 sid_copy( &glist[0], group_sid );
545 n_glist = 1;
547 for ( i=0; i<max_depth && glist; i++ ) {
548 uint32 n_members = 0;
549 char **members = NULL;
550 NTSTATUS nt_status;
552 nt_status = expand_groups( mem_ctx, domain,
553 glist, n_glist,
554 &new_glist, &n_new_glist,
555 &members, &n_members);
556 if ( !NT_STATUS_IS_OK(nt_status) ) {
557 result = False;
558 goto done;
561 /* Add new group members to list */
563 nt_status = add_names_to_list( mem_ctx, &names, &num_names,
564 members, n_members );
565 if ( !NT_STATUS_IS_OK(nt_status) ) {
566 result = False;
567 goto done;
570 TALLOC_FREE( members );
572 /* If we have no more groups to expand, break out
573 early */
575 if (new_glist == NULL)
576 break;
578 /* One more round */
579 TALLOC_FREE(glist);
580 glist = new_glist;
581 n_glist = n_new_glist;
583 TALLOC_FREE( glist );
585 sort_unique_list(&names, &num_names);
587 DEBUG(10, ("looked up %d names\n", num_names));
589 again:
590 /* Add members to list */
592 for (i = 0; i < num_names; i++) {
593 int len;
595 DEBUG(10, ("processing name %s\n", names[i]));
597 len = strlen(names[i]);
599 /* Add to list or calculate buffer length */
601 if (!buf) {
602 buf_len += len + 1; /* List is comma separated */
603 (*num_gr_mem)++;
604 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
605 } else {
606 DEBUG(10, ("appending %s at ndx %d\n",
607 names[i], buf_ndx));
608 parse_add_domuser(&buf[buf_ndx], names[i], &len);
609 buf_ndx += len;
610 buf[buf_ndx] = ',';
611 buf_ndx++;
615 /* Allocate buffer */
617 if (!buf && buf_len != 0) {
618 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
619 DEBUG(1, ("out of memory\n"));
620 result = False;
621 goto done;
623 memset(buf, 0, buf_len);
624 goto again;
627 /* Now we're done */
629 if (buf && buf_ndx > 0) {
630 buf[buf_ndx - 1] = '\0';
633 *gr_mem = buf;
634 *gr_mem_len = buf_len;
636 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem,
637 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
638 result = True;
640 done:
642 talloc_destroy(mem_ctx);
644 DEBUG(10, ("fill_grent_mem returning %d\n", result));
646 return result;
649 static void winbindd_getgrsid( struct winbindd_cli_state *state, DOM_SID group_sid );
651 static void getgrnam_recv( void *private_data, bool success, const DOM_SID *sid,
652 enum lsa_SidType type )
654 struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data;
656 if (!success) {
657 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
658 request_error(state);
659 return;
662 if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) {
663 DEBUG(5,("getgrnam_recv: not a group!\n"));
664 request_error(state);
665 return;
668 winbindd_getgrsid( state, *sid );
672 /* Return a group structure from a group name */
674 void winbindd_getgrnam(struct winbindd_cli_state *state)
676 struct winbindd_domain *domain;
677 fstring name_domain, name_group;
678 char *tmp;
680 /* Ensure null termination */
681 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
683 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
684 state->request.data.groupname));
686 /* Parse domain and groupname */
688 memset(name_group, 0, sizeof(fstring));
690 tmp = state->request.data.groupname;
692 name_domain[0] = '\0';
693 name_group[0] = '\0';
695 parse_domain_user(tmp, name_domain, name_group);
697 /* if no domain or our local domain and no local tdb group, default to
698 * our local domain for aliases */
700 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
701 fstrcpy(name_domain, get_global_sam_name());
704 /* Get info for the domain */
706 if ((domain = find_domain_from_name(name_domain)) == NULL) {
707 DEBUG(3, ("could not get domain sid for domain %s\n",
708 name_domain));
709 request_error(state);
710 return;
712 /* should we deal with users for our domain? */
714 if ( lp_winbind_trusted_domains_only() && domain->primary) {
715 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
716 "getgrnam() for %s\\%s.\n", name_domain, name_group));
717 request_error(state);
718 return;
721 /* Get rid and name type from name */
723 ws_name_replace( name_group, WB_REPLACE_CHAR );
725 winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
726 getgrnam_recv, WINBINDD_GETGRNAM, state );
729 struct getgrsid_state {
730 struct winbindd_cli_state *state;
731 struct winbindd_domain *domain;
732 char *group_name;
733 enum lsa_SidType group_type;
734 uid_t gid;
735 DOM_SID group_sid;
738 static void getgrsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
740 struct getgrsid_state *s =
741 (struct getgrsid_state *)private_data;
742 struct winbindd_domain *domain;
743 size_t gr_mem_len;
744 size_t num_gr_mem;
745 char *gr_mem;
746 fstring dom_name, group_name;
748 if (!success) {
749 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
750 request_error(s->state);
751 return;
754 s->gid = gid;
756 if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) {
757 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
758 request_error(s->state);
759 return;
763 /* Fill in group structure */
765 if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) {
766 DEBUG(1,("Can't find domain from name (%s)\n", dom_name));
767 request_error(s->state);
768 return;
771 if (!fill_grent(&s->state->response.data.gr, dom_name, group_name, gid) ||
772 !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
773 &num_gr_mem, &gr_mem, &gr_mem_len))
775 request_error(s->state);
776 return;
779 s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
781 /* Group membership lives at start of extra data */
783 s->state->response.data.gr.gr_mem_ofs = 0;
785 s->state->response.length += gr_mem_len;
786 s->state->response.extra_data.data = gr_mem;
788 request_ok(s->state);
791 static void getgrsid_lookupsid_recv( void *private_data, bool success,
792 const char *dom_name, const char *name,
793 enum lsa_SidType name_type )
795 struct getgrsid_state *s = (struct getgrsid_state *)private_data;
797 if (!success) {
798 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
799 request_error(s->state);
800 return;
803 /* either it's a domain group, a domain local group, or a
804 local group in an internal domain */
806 if ( !( (name_type==SID_NAME_DOM_GRP) ||
807 ((name_type==SID_NAME_ALIAS) &&
808 (s->domain->primary || s->domain->internal)) ) )
810 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n",
811 dom_name, name, name_type));
812 request_error(s->state);
813 return;
816 if ( (s->group_name = talloc_asprintf( s->state->mem_ctx,
817 "%s%c%s",
818 dom_name,
819 *lp_winbind_separator(),
820 name)) == NULL )
822 DEBUG(1, ("getgrsid_lookupsid_recv: talloc_asprintf() Failed!\n"));
823 request_error(s->state);
824 return;
827 s->group_type = name_type;
829 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
830 getgrsid_sid2gid_recv, s);
833 static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid )
835 struct getgrsid_state *s;
837 if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) {
838 DEBUG(0, ("talloc failed\n"));
839 request_error(state);
840 return;
843 s->state = state;
845 if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) {
846 DEBUG(3, ("Could not find domain for sid %s\n",
847 sid_string_dbg(&group_sid)));
848 request_error(state);
849 return;
852 sid_copy(&s->group_sid, &group_sid);
854 winbindd_lookupsid_async( s->state->mem_ctx, &group_sid,
855 getgrsid_lookupsid_recv, s );
859 static void getgrgid_recv(void *private_data, bool success, const char *sid)
861 struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
862 enum lsa_SidType name_type;
863 DOM_SID group_sid;
865 if (success) {
866 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
867 (unsigned long)(state->request.data.gid), sid));
869 string_to_sid(&group_sid, sid);
870 winbindd_getgrsid(state, group_sid);
871 return;
874 /* Ok, this might be "ours", i.e. an alias */
875 if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
876 lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
877 (name_type == SID_NAME_ALIAS)) {
878 /* Hey, got an alias */
879 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
880 (unsigned long)(state->request.data.gid), sid));
881 winbindd_getgrsid(state, group_sid);
882 return;
885 DEBUG(1, ("could not convert gid %lu to sid\n",
886 (unsigned long)state->request.data.gid));
887 request_error(state);
890 /* Return a group structure from a gid number */
891 void winbindd_getgrgid(struct winbindd_cli_state *state)
893 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
894 (unsigned long)state->request.data.gid));
896 /* always use the async interface */
897 winbindd_gid2sid_async(state->mem_ctx, state->request.data.gid, getgrgid_recv, state);
901 * set/get/endgrent functions
904 /* "Rewind" file pointer for group database enumeration */
906 static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
908 struct winbindd_domain *domain;
910 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
912 /* Check user has enabled this */
914 if (!lp_winbind_enum_groups()) {
915 return False;
918 /* Free old static data if it exists */
920 if (state->getgrent_state != NULL) {
921 free_getent_state(state->getgrent_state);
922 state->getgrent_state = NULL;
925 /* Create sam pipes for each domain we know about */
927 for (domain = domain_list(); domain != NULL; domain = domain->next) {
928 struct getent_state *domain_state;
930 /* Create a state record for this domain */
932 /* don't add our domaina if we are a PDC or if we
933 are a member of a Samba domain */
935 if ( lp_winbind_trusted_domains_only() && domain->primary )
937 continue;
941 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
942 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
943 return False;
946 ZERO_STRUCTP(domain_state);
948 fstrcpy(domain_state->domain_name, domain->name);
950 /* Add to list of open domains */
952 DLIST_ADD(state->getgrent_state, domain_state);
955 state->getgrent_initialized = True;
956 return True;
959 void winbindd_setgrent(struct winbindd_cli_state *state)
961 if (winbindd_setgrent_internal(state)) {
962 request_ok(state);
963 } else {
964 request_error(state);
968 /* Close file pointer to ntdom group database */
970 void winbindd_endgrent(struct winbindd_cli_state *state)
972 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
974 free_getent_state(state->getgrent_state);
975 state->getgrent_initialized = False;
976 state->getgrent_state = NULL;
977 request_ok(state);
980 /* Get the list of domain groups and domain aliases for a domain. We fill in
981 the sam_entries and num_sam_entries fields with domain group information.
982 Return True if some groups were returned, False otherwise. */
984 bool get_sam_group_entries(struct getent_state *ent)
986 NTSTATUS status;
987 uint32 num_entries;
988 struct acct_info *name_list = NULL;
989 TALLOC_CTX *mem_ctx;
990 bool result = False;
991 struct acct_info *sam_grp_entries = NULL;
992 struct winbindd_domain *domain;
994 if (ent->got_sam_entries)
995 return False;
997 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
998 ent->domain_name))) {
999 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
1000 return False;
1003 /* Free any existing group info */
1005 SAFE_FREE(ent->sam_entries);
1006 ent->num_sam_entries = 0;
1007 ent->got_sam_entries = True;
1009 /* Enumerate domain groups */
1011 num_entries = 0;
1013 if (!(domain = find_domain_from_name(ent->domain_name))) {
1014 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
1015 goto done;
1018 /* always get the domain global groups */
1020 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
1022 if (!NT_STATUS_IS_OK(status)) {
1023 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
1024 result = False;
1025 goto done;
1028 /* Copy entries into return buffer */
1030 if (num_entries) {
1031 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
1032 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
1033 num_entries));
1034 result = False;
1035 goto done;
1037 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
1040 ent->num_sam_entries = num_entries;
1042 /* get the domain local groups if we are a member of a native win2k domain
1043 and are not using LDAP to get the groups */
1045 if ( ( lp_security() != SEC_ADS && domain->native_mode
1046 && domain->primary) || domain->internal )
1048 DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n",
1049 domain->native_mode ? "Native Mode 2k":"BUILTIN or local"));
1051 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
1053 if ( !NT_STATUS_IS_OK(status) ) {
1054 DEBUG(3,("get_sam_group_entries: "
1055 "Failed to enumerate "
1056 "domain local groups with error %s!\n",
1057 nt_errstr(status)));
1058 num_entries = 0;
1060 else
1061 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
1063 /* Copy entries into return buffer */
1065 if ( num_entries ) {
1066 if ( !(name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
1068 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
1069 num_entries));
1070 result = False;
1071 goto done;
1074 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
1075 num_entries * sizeof(struct acct_info) );
1078 ent->num_sam_entries += num_entries;
1082 /* Fill in remaining fields */
1084 ent->sam_entries = name_list;
1085 ent->sam_entry_index = 0;
1087 result = (ent->num_sam_entries > 0);
1089 done:
1090 talloc_destroy(mem_ctx);
1092 return result;
1095 /* Fetch next group entry from ntdom database */
1097 #define MAX_GETGRENT_GROUPS 500
1099 void winbindd_getgrent(struct winbindd_cli_state *state)
1101 struct getent_state *ent;
1102 struct winbindd_gr *group_list = NULL;
1103 int num_groups, group_list_ndx, gr_mem_list_len = 0;
1104 char *gr_mem_list = NULL;
1106 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1108 /* Check user has enabled this */
1110 if (!lp_winbind_enum_groups()) {
1111 request_error(state);
1112 return;
1115 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
1117 if (num_groups == 0) {
1118 request_error(state);
1119 return;
1122 if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) {
1123 request_error(state);
1124 return;
1127 memset(state->response.extra_data.data, '\0',
1128 num_groups * sizeof(struct winbindd_gr) );
1130 state->response.data.num_entries = 0;
1132 group_list = (struct winbindd_gr *)state->response.extra_data.data;
1134 if (!state->getgrent_initialized)
1135 winbindd_setgrent_internal(state);
1137 if (!(ent = state->getgrent_state)) {
1138 request_error(state);
1139 return;
1142 /* Start sending back groups */
1144 for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1145 struct acct_info *name_list = NULL;
1146 fstring domain_group_name;
1147 uint32 result;
1148 gid_t group_gid;
1149 size_t gr_mem_len;
1150 char *gr_mem;
1151 DOM_SID group_sid;
1152 struct winbindd_domain *domain;
1154 /* Do we need to fetch another chunk of groups? */
1156 tryagain:
1158 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1159 ent->sam_entry_index, ent->num_sam_entries));
1161 if (ent->num_sam_entries == ent->sam_entry_index) {
1163 while(ent && !get_sam_group_entries(ent)) {
1164 struct getent_state *next_ent;
1166 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
1168 /* Free state information for this domain */
1170 SAFE_FREE(ent->sam_entries);
1172 next_ent = ent->next;
1173 DLIST_REMOVE(state->getgrent_state, ent);
1175 SAFE_FREE(ent);
1176 ent = next_ent;
1179 /* No more domains */
1181 if (!ent)
1182 break;
1185 name_list = (struct acct_info *)ent->sam_entries;
1187 if (!(domain =
1188 find_domain_from_name(ent->domain_name))) {
1189 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
1190 result = False;
1191 goto done;
1194 /* Lookup group info */
1196 sid_copy(&group_sid, &domain->sid);
1197 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1199 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid))) {
1200 union unid_t id;
1201 enum lsa_SidType type;
1203 DEBUG(10, ("SID %s not in idmap\n",
1204 sid_string_dbg(&group_sid)));
1206 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1207 DEBUG(1, ("could not look up gid for group "
1208 "%s\n",
1209 name_list[ent->sam_entry_index].acct_name));
1210 ent->sam_entry_index++;
1211 goto tryagain;
1214 if ((type != SID_NAME_DOM_GRP) &&
1215 (type != SID_NAME_ALIAS) &&
1216 (type != SID_NAME_WKN_GRP)) {
1217 DEBUG(1, ("Group %s is a %s, not a group\n",
1218 sid_type_lookup(type),
1219 name_list[ent->sam_entry_index].acct_name));
1220 ent->sam_entry_index++;
1221 goto tryagain;
1223 group_gid = id.gid;
1226 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
1227 (unsigned long)name_list[ent->sam_entry_index].rid));
1229 /* Fill in group entry */
1231 fill_domain_username(domain_group_name, ent->domain_name,
1232 name_list[ent->sam_entry_index].acct_name, True);
1234 result = fill_grent(&group_list[group_list_ndx],
1235 ent->domain_name,
1236 name_list[ent->sam_entry_index].acct_name,
1237 group_gid);
1239 /* Fill in group membership entry */
1241 if (result) {
1242 size_t num_gr_mem = 0;
1243 DOM_SID member_sid;
1244 group_list[group_list_ndx].num_gr_mem = 0;
1245 gr_mem = NULL;
1246 gr_mem_len = 0;
1248 /* Get group membership */
1249 if (state->request.cmd == WINBINDD_GETGRLST) {
1250 result = True;
1251 } else {
1252 sid_copy(&member_sid, &domain->sid);
1253 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1254 result = fill_grent_mem(
1255 domain,
1256 NULL,
1257 &member_sid,
1258 SID_NAME_DOM_GRP,
1259 &num_gr_mem,
1260 &gr_mem, &gr_mem_len);
1262 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1266 if (result) {
1267 /* Append to group membership list */
1268 gr_mem_list = (char *)SMB_REALLOC(
1269 gr_mem_list, gr_mem_list_len + gr_mem_len);
1271 if (!gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
1272 DEBUG(0, ("out of memory\n"));
1273 gr_mem_list_len = 0;
1274 break;
1277 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1278 gr_mem_list_len, (unsigned int)gr_mem_len));
1280 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1281 gr_mem_len);
1283 SAFE_FREE(gr_mem);
1285 group_list[group_list_ndx].gr_mem_ofs =
1286 gr_mem_list_len;
1288 gr_mem_list_len += gr_mem_len;
1291 ent->sam_entry_index++;
1293 /* Add group to return list */
1295 if (result) {
1297 DEBUG(10, ("adding group num_entries = %d\n",
1298 state->response.data.num_entries));
1300 group_list_ndx++;
1301 state->response.data.num_entries++;
1303 state->response.length +=
1304 sizeof(struct winbindd_gr);
1306 } else {
1307 DEBUG(0, ("could not lookup domain group %s\n",
1308 domain_group_name));
1312 /* Copy the list of group memberships to the end of the extra data */
1314 if (group_list_ndx == 0)
1315 goto done;
1317 state->response.extra_data.data = SMB_REALLOC(
1318 state->response.extra_data.data,
1319 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1321 if (!state->response.extra_data.data) {
1322 DEBUG(0, ("out of memory\n"));
1323 group_list_ndx = 0;
1324 SAFE_FREE(gr_mem_list);
1325 request_error(state);
1326 return;
1329 memcpy(&((char *)state->response.extra_data.data)
1330 [group_list_ndx * sizeof(struct winbindd_gr)],
1331 gr_mem_list, gr_mem_list_len);
1333 state->response.length += gr_mem_list_len;
1335 DEBUG(10, ("returning %d groups, length = %d\n",
1336 group_list_ndx, gr_mem_list_len));
1338 /* Out of domains */
1340 done:
1342 SAFE_FREE(gr_mem_list);
1344 if (group_list_ndx > 0)
1345 request_ok(state);
1346 else
1347 request_error(state);
1350 /* List domain groups without mapping to unix ids */
1351 void winbindd_list_groups(struct winbindd_cli_state *state)
1353 winbindd_list_ent(state, LIST_GROUPS);
1356 /* Get user supplementary groups. This is much quicker than trying to
1357 invert the groups database. We merge the groups from the gids and
1358 other_sids info3 fields as trusted domain, universal group
1359 memberships, and nested groups (win2k native mode only) are not
1360 returned by the getgroups RPC call but are present in the info3. */
1362 struct getgroups_state {
1363 struct winbindd_cli_state *state;
1364 struct winbindd_domain *domain;
1365 char *domname;
1366 char *username;
1367 DOM_SID user_sid;
1369 const DOM_SID *token_sids;
1370 size_t i, num_token_sids;
1372 gid_t *token_gids;
1373 size_t num_token_gids;
1376 static void getgroups_usersid_recv(void *private_data, bool success,
1377 const DOM_SID *sid, enum lsa_SidType type);
1378 static void getgroups_tokensids_recv(void *private_data, bool success,
1379 DOM_SID *token_sids, size_t num_token_sids);
1380 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid);
1382 void winbindd_getgroups(struct winbindd_cli_state *state)
1384 struct getgroups_state *s;
1386 /* Ensure null termination */
1387 state->request.data.username
1388 [sizeof(state->request.data.username)-1]='\0';
1390 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1391 state->request.data.username));
1393 /* Parse domain and username */
1395 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1396 if (s == NULL) {
1397 DEBUG(0, ("talloc failed\n"));
1398 request_error(state);
1399 return;
1402 s->state = state;
1404 ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
1406 if (!parse_domain_user_talloc(state->mem_ctx,
1407 state->request.data.username,
1408 &s->domname, &s->username)) {
1409 DEBUG(5, ("Could not parse domain user: %s\n",
1410 state->request.data.username));
1412 /* error out if we do not have nested group support */
1414 if ( !lp_winbind_nested_groups() ) {
1415 request_error(state);
1416 return;
1419 s->domname = talloc_strdup( state->mem_ctx, get_global_sam_name() );
1420 s->username = talloc_strdup( state->mem_ctx, state->request.data.username );
1423 /* Get info for the domain (either by short domain name or
1424 DNS name in the case of a UPN) */
1426 s->domain = find_domain_from_name_noinit(s->domname);
1427 if (!s->domain) {
1428 char *p = strchr(s->username, '@');
1430 if (p) {
1431 s->domain = find_domain_from_name_noinit(p+1);
1436 if (s->domain == NULL) {
1437 DEBUG(7, ("could not find domain entry for domain %s\n",
1438 s->domname));
1439 request_error(state);
1440 return;
1443 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1444 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1445 "getgroups() for %s\\%s.\n", s->domname,
1446 s->username));
1447 request_error(state);
1448 return;
1451 /* Get rid and name type from name. The following costs 1 packet */
1453 winbindd_lookupname_async(state->mem_ctx, s->domname, s->username,
1454 getgroups_usersid_recv, WINBINDD_GETGROUPS, s);
1457 static void getgroups_usersid_recv(void *private_data, bool success,
1458 const DOM_SID *sid, enum lsa_SidType type)
1460 struct getgroups_state *s =
1461 (struct getgroups_state *)private_data;
1463 if ((!success) ||
1464 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1465 request_error(s->state);
1466 return;
1469 sid_copy(&s->user_sid, sid);
1471 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1472 getgroups_tokensids_recv, s);
1475 static void getgroups_tokensids_recv(void *private_data, bool success,
1476 DOM_SID *token_sids, size_t num_token_sids)
1478 struct getgroups_state *s =
1479 (struct getgroups_state *)private_data;
1481 /* We need at least the user sid and the primary group in the token,
1482 * otherwise it's an error */
1484 if ((!success) || (num_token_sids < 2)) {
1485 request_error(s->state);
1486 return;
1489 s->token_sids = token_sids;
1490 s->num_token_sids = num_token_sids;
1491 s->i = 0;
1493 s->token_gids = NULL;
1494 s->num_token_gids = 0;
1496 getgroups_sid2gid_recv(s, False, 0);
1499 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid)
1501 struct getgroups_state *s =
1502 (struct getgroups_state *)private_data;
1504 if (success) {
1505 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1506 &s->token_gids,
1507 &s->num_token_gids)) {
1508 return;
1512 if (s->i < s->num_token_sids) {
1513 const DOM_SID *sid = &s->token_sids[s->i];
1514 s->i += 1;
1516 if (sid_equal(sid, &s->user_sid)) {
1517 getgroups_sid2gid_recv(s, False, 0);
1518 return;
1521 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1522 getgroups_sid2gid_recv, s);
1523 return;
1526 s->state->response.data.num_entries = s->num_token_gids;
1527 if (s->num_token_gids) {
1528 /* s->token_gids are talloced */
1529 s->state->response.extra_data.data = smb_xmemdup(s->token_gids, s->num_token_gids * sizeof(gid_t));
1530 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1532 request_ok(s->state);
1535 /* Get user supplementary sids. This is equivalent to the
1536 winbindd_getgroups() function but it involves a SID->SIDs mapping
1537 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1538 idmap. This call is designed to be used with applications that need
1539 to do ACL evaluation themselves. Note that the cached info3 data is
1540 not used
1542 this function assumes that the SID that comes in is a user SID. If
1543 you pass in another type of SID then you may get unpredictable
1544 results.
1547 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1548 size_t num_sids);
1550 void winbindd_getusersids(struct winbindd_cli_state *state)
1552 DOM_SID *user_sid;
1554 /* Ensure null termination */
1555 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1557 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1558 if (user_sid == NULL) {
1559 DEBUG(1, ("talloc failed\n"));
1560 request_error(state);
1561 return;
1564 if (!string_to_sid(user_sid, state->request.data.sid)) {
1565 DEBUG(1, ("Could not get convert sid %s from string\n",
1566 state->request.data.sid));
1567 request_error(state);
1568 return;
1571 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1572 state);
1575 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1576 size_t num_sids)
1578 struct winbindd_cli_state *state =
1579 (struct winbindd_cli_state *)private_data;
1580 char *ret = NULL;
1581 unsigned ofs, ret_size = 0;
1582 size_t i;
1584 if (!success) {
1585 request_error(state);
1586 return;
1589 /* work out the response size */
1590 for (i = 0; i < num_sids; i++) {
1591 fstring s;
1592 sid_to_fstring(s, &sids[i]);
1593 ret_size += strlen(s) + 1;
1596 /* build the reply */
1597 ret = (char *)SMB_MALLOC(ret_size);
1598 if (!ret) {
1599 DEBUG(0, ("malloc failed\n"));
1600 request_error(state);
1601 return;
1603 ofs = 0;
1604 for (i = 0; i < num_sids; i++) {
1605 fstring s;
1606 sid_to_fstring(s, &sids[i]);
1607 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1608 ofs += strlen(ret+ofs) + 1;
1611 /* Send data back to client */
1612 state->response.data.num_entries = num_sids;
1613 state->response.extra_data.data = ret;
1614 state->response.length += ret_size;
1615 request_ok(state);
1618 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1620 DOM_SID user_sid;
1621 struct winbindd_domain *domain;
1623 /* Ensure null termination */
1624 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1626 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1627 DEBUG(1, ("Could not get convert sid %s from string\n",
1628 state->request.data.sid));
1629 request_error(state);
1630 return;
1633 /* Get info for the domain */
1634 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1635 DEBUG(0,("could not find domain entry for sid %s\n",
1636 sid_string_dbg(&user_sid)));
1637 request_error(state);
1638 return;
1641 sendto_domain(state, domain);
1644 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1645 struct winbindd_cli_state *state)
1647 DOM_SID user_sid;
1648 NTSTATUS status;
1650 char *sidstring;
1651 ssize_t len;
1652 DOM_SID *groups;
1653 uint32 num_groups;
1655 /* Ensure null termination */
1656 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1658 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1659 DEBUG(1, ("Could not get convert sid %s from string\n",
1660 state->request.data.sid));
1661 return WINBINDD_ERROR;
1664 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1665 &user_sid, &num_groups,
1666 &groups);
1667 if (!NT_STATUS_IS_OK(status))
1668 return WINBINDD_ERROR;
1670 if (num_groups == 0) {
1671 state->response.data.num_entries = 0;
1672 state->response.extra_data.data = NULL;
1673 return WINBINDD_OK;
1676 if (!print_sidlist(state->mem_ctx, groups, num_groups, &sidstring, &len)) {
1677 DEBUG(0, ("talloc failed\n"));
1678 return WINBINDD_ERROR;
1681 state->response.extra_data.data = SMB_STRDUP(sidstring);
1682 if (!state->response.extra_data.data) {
1683 return WINBINDD_ERROR;
1685 state->response.length += len+1;
1686 state->response.data.num_entries = num_groups;
1688 return WINBINDD_OK;